<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Blog of gitroot.dev</title>
    <link>https://gitroot.dev/</link>
    <description>All blogs articles of gitroot.dev</description>
    <language>en</language>
    <generator>Pollen plugin - GitRoot</generator>
    <atom:link href="https://gitroot.dev//rss/blog.xml" rel="self" type="application/rss+xml" />
    <!-- next -->
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.5.0-plugins.md</link><title>0.5.0 plugins</title><pubDate>2026/10/30</pubDate><description>&lt;h1 id=&quot;what-is-gitroot-without-its-plugins&quot;&gt;What is GitRoot without its plugins?&lt;/h1&gt;

&lt;p&gt;In the &lt;a href=&quot;blog/version-0.5.0.html&quot;&gt;release 0.5.0 blog post&lt;/a&gt; I have only talked about the GitRoot core code. But GitRoot is a forge where plugins take a lot of space. In the future I want to decouple GitRoot and its plugins. I hope to stabilize the plugin-sdk, release them, so that you will be able to create your own plugins.&lt;/p&gt;

&lt;p&gt;But today, GitRoot and &lt;a href=&quot;plugins/scope_gitroot.html&quot;&gt;officials plugins&lt;/a&gt; are linked by the version. So let me explain what they can do in this new version.&lt;/p&gt;

&lt;h2 id=&quot;all-plugins-are-compatible-with-the-0-5-0&quot;&gt;All plugins are compatible with the 0.5.0&lt;/h2&gt;

&lt;p&gt;All plugins are compatible with the 0.5.0 plugin-sdk. What does that mean? Every time you see a &lt;strong&gt;BREAKING&lt;/strong&gt; and &lt;code&gt;plugin&lt;/code&gt; in the &lt;a href=&quot;CHANGELOG.html&quot;&gt;changelog&lt;/a&gt;, it means I had to change the code of the plugin-sdk and recompile plugins with that new code (sometimes I need to change the code of the plugin, sometimes not). So when you see &lt;code&gt;compatible with gitroot plugin-sdk 0.5.0&lt;/code&gt; in the changelog of a plugin, it means that the plugin is good to run inside this GitRoot version.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ll present here only the most important features, for details you can read the changelog of each plugin.&lt;/p&gt;

&lt;h2 id=&quot;firefly-in-zig&quot;&gt;Firefly in zig&lt;/h2&gt;

&lt;p&gt;First, let me introduce &lt;a href=&quot;plugins/name/firefly.html&quot;&gt;Firefly&lt;/a&gt;: add URLs to call and Firefly will generate beautiful status pages for them. Yes, it&amp;rsquo;s a status page directly integrated into your forge, so no need to depend on a third-party service. Of course, it&amp;rsquo;s useful only if the app observed is not on the same host as your forge. So it&amp;rsquo;s totally useless for GitRoot. But as an example, you can see the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/status/&quot;&gt;status page of gitroot on gitroot&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://gitroot.dev/branches/v0.5-plugins/blog/version-0.5.0-plugins-firefly.png&quot; alt=&quot;Screenshot of the firefly plugin, it&amp;rsquo;s a status plugin, we see gitroot.dev is up and running, also @forge@gitroot.dev (gotosocial of gitroot) is running.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;m proud because it&amp;rsquo;s my first code in the &lt;a target=&quot;_blank&quot; href=&quot;https://ziglang.org/&quot;&gt;zig&lt;/a&gt; language. Not everything is perfect, of course, but I love the language itself, so I will make other plugins and rework them when needed. If you are Zig lovers too, don&amp;rsquo;t hesitate to help me.&lt;/p&gt;

&lt;h2 id=&quot;silo-lots-of-new-things&quot;&gt;Silo, lots of new things&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;plugins/name/silo.html&quot;&gt;Silo&lt;/a&gt;: from files and metadata, Silo generates indexes to find files more easily. I have added a lot of features into this milestone.&lt;/p&gt;

&lt;p&gt;You can now &lt;code&gt;sort&lt;/code&gt; the rendered list by the files which each item links to. I use it to render all the &lt;code&gt;TODO&lt;/code&gt;s in GitRoot, &lt;a href=&quot;boards/todos.html&quot;&gt;in the todo board&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now Silo can render the list directly to the web. Useful to not encumber the git repositories with redundant lists. For example, I use it to render plugins in &lt;a href=&quot;plugins&quot;&gt;alphabetical order&lt;/a&gt; or by &lt;a href=&quot;plugins/scope_gitroot.html&quot;&gt;scope&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Furthermore, as you can see in the &lt;a href=&quot;plugins&quot;&gt;plugins page&lt;/a&gt;, Silo can now render lists by alphabetical group. To render them, I have added a new property &lt;code&gt;groupBy&lt;/code&gt; which allows you to tell Silo which property you want to group your list items by.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;groupBy&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;desciption&lt;/code&gt; and &lt;code&gt;to&lt;/code&gt; properties can contain &lt;code&gt;$variable&lt;/code&gt;. These variables will be interpreted when Silo renders the list. So, for example, if you want to render the title of a list with a special metadata of the content, you can:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;selects&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;purl: pkg:gitroot/[^/]+/([^@]+)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;purl: pkg:gitroot/([^/]+)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;title&lt;/span&gt;: Plugins for scope $select[1]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means that you select the scope and name of the PURL plugin and render the name into the title. A way to add metadata into the rendered list is to use the new &lt;code&gt;metadata&lt;/code&gt; property.&lt;/p&gt;
&lt;blockquote style=&quot;border-color: #006ac7&quot;&gt;

&lt;p&gt;The silo plugin use the &lt;a target=&quot;_blank&quot; href=&quot;https://pkg.go.dev/regexp&quot;&gt;standard regexp&lt;/a&gt; from go to extract selects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With all of that, dynamic boards can be created. From files to index files with metadata, you can create indexes of indexes. An example is the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/plugins/scope.html&quot;&gt;scope page&lt;/a&gt;. Silo takes all plugins from the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/plugins/name/index.html&quot;&gt;plugins/name directory&lt;/a&gt;, generates a page which is an index of all scopes, and then generates a page for each. Today only the &lt;a href=&quot;plugins/scope_gitroot.html&quot;&gt;gitroot scope&lt;/a&gt; exists, but I hope for a lot more in the future.&lt;/p&gt;

&lt;p&gt;Not related, but a good addition to the Silo plugin is the &lt;code&gt;custom&lt;/code&gt; data format. It permits to render each element in the format you want (by default, only list and table were possible). I use it to render the &lt;a href=&quot;blog&quot;&gt;blog posts index&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote style=&quot;border-color: #006ac7&quot;&gt;

&lt;p&gt;To find examples on how to configure all of that, look at the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/.gitroot/plugins.yml.html#L60&quot;&gt;silo configuration&lt;/a&gt; of gitroot.dev instance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;apex-is-the-web-view&quot;&gt;Apex is the web view&lt;/h2&gt;

&lt;p&gt;A lot of tiny, but useful things have been added to the &lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex&lt;/a&gt; plugin. Apex is the plugin that renders the files from your git repository to the web. You configure the &lt;code&gt;header&lt;/code&gt;, &lt;code&gt;footer&lt;/code&gt;, &lt;code&gt;menu&lt;/code&gt;, and it takes care of assembling all of that with your &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; content. Today it only renders HTML and markdown (through the &lt;a href=&quot;plugins/name/apex_markdown.html&quot;&gt;apex_markdown plugin&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;So, the first addition is to export a &lt;code&gt;renderHtml&lt;/code&gt; function. With that in place, all other plugins can render something with your header, menu, footer and the right CSS. Without it, most plugins rendered only the &lt;code&gt;content&lt;/code&gt; of the page withtout &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;. For example, see what Firefly looks like without Apex:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://gitroot.dev/branches/v0.5-plugins/blog/version-0.5.0-plugins-firefly-no-apex.png&quot; alt=&quot;Screenshot of firefly plugin without css, so mostly black on white page&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A new &lt;code&gt;layout&lt;/code&gt; has been added: &lt;code&gt;slide&lt;/code&gt;. Previously, the property &lt;code&gt;layout&lt;/code&gt; could be filled with a path and Apex used it (if it didn&amp;rsquo;t exist, Apex created it with the default one inside). But if instead of a path you set &lt;code&gt;issue&lt;/code&gt;, Apex will render all matching paths with a particular layout: see &lt;a href=&quot;issues/close/v0.5.0/88d7-anonymous-can-create-branch.html&quot;&gt;any issue&lt;/a&gt;, Apex has added metadata before the page, and grouped all content inside an &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; instead of a &lt;code&gt;&amp;lt;br&amp;gt;&lt;/code&gt; like you can see in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/issues/close/v0.5.0/88d7-anonymous-can-create-branch.md.html&quot;&gt;original markdown page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Following this strategy, the new &lt;code&gt;slide&lt;/code&gt; layout renders the page with the &lt;a href=&quot;app/plugins/apex/resources/styles/slide.css&quot;&gt;slide.css&lt;/a&gt; and groups all page breaks into &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; as required by the CSS.&lt;/p&gt;

&lt;p&gt;If Apex renders markdown through the apex_markdown plugin, how is that possible? All the magic resides in a new property added in the apex_markdown plugin. This property is &lt;code&gt;horizontalRuleMetadatas&lt;/code&gt;, with that, Apex can instruct the markdown renderer how to render page breaks.&lt;/p&gt;

&lt;p&gt;Another addition to apex_markdown is the ability to render callouts for blockquotes and details/summary. To render a callout, use this in your markdown:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;text-decoration:underline&quot;&gt;[!NOTE]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;text-decoration:underline&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;text-decoration:underline&quot;&gt;This a note callout.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which renders on the web as:&lt;/p&gt;
&lt;blockquote style=&quot;border-color: #006ac7&quot;&gt;

&lt;p&gt;This a note callout.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Today, the possibilities are (but in the future I want to make them configurable):&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;var&lt;/span&gt; calloutColor = &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;map&lt;/span&gt;[&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;]&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;[!NOTE]&amp;#34;&lt;/span&gt;:    &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;#006ac7&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;[!WARNING]&amp;#34;&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;#be4700&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;[!ERROR]&amp;#34;&lt;/span&gt;:   &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;#f00&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Following the same philosophy, to render details/summary:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;text-decoration:underline&quot;&gt;[!DETAILS] This is a summary
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;text-decoration:underline&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;text-decoration:underline&quot;&gt;This is the details
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which renders on the web as:&lt;/p&gt;
&lt;details&gt;&lt;summary&gt;This is a summary&lt;/summary&gt;

&lt;p&gt;This is the details&lt;/p&gt;
&lt;/details&gt;

&lt;p&gt;Finally, in a last-minute change before the release, I have added the metadata &lt;code&gt;toc: true&lt;/code&gt; to be able to render a table of contents, because the &lt;a href=&quot;blog/version-0.5.0.html&quot;&gt;v0.5.0 article&lt;/a&gt; was too long.&lt;/p&gt;

&lt;h2 id=&quot;grafter-diff&quot;&gt;Grafter diff&lt;/h2&gt;

&lt;p&gt;An important (in UX, not size) change in the &lt;a href=&quot;plugins/name/grafter.html&quot;&gt;grafter plugin&lt;/a&gt; is that it now renders commit diffs from the start. Grafter is the plugin responsible for managing contributions, it permits discussing what has been done and finally merging (or not) the contribution.&lt;/p&gt;

&lt;p&gt;Previously, Grafter displayed diffs only after the property &lt;code&gt;status&lt;/code&gt; was set to &lt;code&gt;review&lt;/code&gt;. But that was confusing. Now, from the start, it shows diffs.&lt;/p&gt;

&lt;p&gt;And now that the Apex plugin can render summary/details, Grafter uses it to render reports from other plugins. You can see the result in the graft &lt;a href=&quot;grafts/v0.5-plugins.html&quot;&gt;of this post&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;pollen-render-content&quot;&gt;Pollen render content&lt;/h2&gt;

&lt;p&gt;If you use &lt;a href=&quot;plugins/name/pollen.html&quot;&gt;pollen&lt;/a&gt;, you can delete the property &lt;code&gt;link&lt;/code&gt; as it is calculated directly from the forge configuration. Pollen is the plugin responsible for communicating with the outside of your forge. Today it uses only RSS.&lt;/p&gt;

&lt;p&gt;But the biggest change is the ability to render the content of files in addition to the commits. Previously, Pollen rendered only commit metadata (title, description, author&amp;hellip;). Now it can render the content of files to have a true RSS for your issues, grafts, or even blog posts. Don&amp;rsquo;t forget to subscribe to the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/rss/blog.xml&quot;&gt;blog rss&lt;/a&gt; of this instance.&lt;/p&gt;

&lt;h2 id=&quot;what-next&quot;&gt;What next?&lt;/h2&gt;

&lt;p&gt;One big thing I wanted to do before the &lt;a href=&quot;blog/version-0.5.0.html&quot;&gt;0.5.0 release&lt;/a&gt; (but ran out of time) was to change how Apex renders links. Today all links are absolute with a trick in the html page (&lt;code&gt;&amp;lt;base href=&amp;quot;https://gitroot.dev/&amp;quot;&amp;gt;&lt;/code&gt;), I have done that because the &lt;a href=&quot;blog/no-existing.html&quot;&gt;404 page&lt;/a&gt; needs to be rendered everywhere in the repository. Unfortunately, that makes too many bugs with bad linking. So I want to revert that as priority one (maybe keep the trick only for the 404 page).&lt;/p&gt;

&lt;p&gt;After that, I want to better render Git in the web. A lot of missing concepts need to be rendered, &lt;code&gt;commit&lt;/code&gt;, &lt;code&gt;branch&lt;/code&gt; are the first ones. So I will add an &lt;code&gt;apex_git&lt;/code&gt; plugin. And if I have time, an &lt;code&gt;apex_gitroot&lt;/code&gt; plugin to render specific GitRoot files like &lt;code&gt;users.yml&lt;/code&gt;, &lt;code&gt;plugins.yml&lt;/code&gt; or &lt;code&gt;repositories.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I have grouped multiple related issues in the &lt;a href=&quot;boards/status_apex_git.html&quot;&gt;apex board&lt;/a&gt;. That will be my priority, but of course, as I change my mind a lot, that&amp;rsquo;s not a promise of what will be done.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Special thanks to &lt;code&gt;Ryuno-Ki&lt;/code&gt; from the &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/#gitroot:matrix.org&quot;&gt;#gitroot:matrix.org&lt;/a&gt; Matrix room for reviewing and correcting this article. Want to help with future posts? Join us in the room!&lt;/em&gt;&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.5.0-plugins.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.5.0.md</link><title>0.5.0</title><pubDate>2026/10/05</pubDate><description>&lt;nav&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#move-slowly-and-take-care-of-things&quot;&gt;Move slowly, and take care of things&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#breaking-purl&quot;&gt;Breaking PURL&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#a-lot-of-people-have-discovered-gitroot&quot;&gt;A lot of people have discovered GitRoot&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#not-conclusive&quot;&gt;Not conclusive&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#before-it-s-too-late&quot;&gt;Before it&amp;rsquo;s too late&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#my-new-favorite-language-until-the-next-one&quot;&gt;My new favorite language&amp;hellip;. until the next one&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#plugins-needs-more-power&quot;&gt;Plugins needs more power&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#sftp-to-save-the-web&quot;&gt;Sftp to save the web&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#how-many-bugs&quot;&gt;How many bugs?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#in-depth-details&quot;&gt;In-depth Details&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#purl-breaking-details&quot;&gt;PURL breaking details&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#gitroot-communication&quot;&gt;GitRoot communication&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#sdkgenerator-full-story&quot;&gt;SdkGenerator full story&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#max-anonymous-branches&quot;&gt;Max anonymous branches&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#zig-plugin-sdk&quot;&gt;Zig plugin-sdk&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#pluginsdk-improvements&quot;&gt;PluginSdk improvements&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#sftp-feature&quot;&gt;Sftp feature&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#some-interesting-bug-fixes&quot;&gt;Some interesting bug fixes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#what-next&quot;&gt;What next?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;/nav&gt;

&lt;h1 id=&quot;move-slowly-and-take-care-of-things&quot;&gt;Move slowly, and take care of things&lt;/h1&gt;

&lt;p&gt;These days, everything moves too fast. They push thousands of lines of AI generated code every hour, deploying it without even understanding how it works. But with GitRoot, I want to do the exact opposite. I want to take my time, savor every architectural choice, and thoroughly enjoy writing every line of code by hand. It&amp;rsquo;s a project that is deeply close to my heart: a space for freedom far away from the rat race of metrics. So yes, we move at our own pace: &lt;code&gt;slowly, and take care of things&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote style=&quot;border-color: #006ac7&quot;&gt;

&lt;p&gt;Thanks to &lt;a target=&quot;_blank&quot; href=&quot;https://pouet.chapril.org/@lonugem&quot;&gt;lonugem&lt;/a&gt; for the title idea&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes&lt;/h2&gt;

&lt;h3 id=&quot;breaking-purl&quot;&gt;Breaking PURL&lt;/h3&gt;

&lt;p&gt;I&amp;rsquo;m sorry to say that the first feature I want to talk about is a big breaking one. I know, I know, on the homepage of GitRoot I say &amp;ldquo;no headaches to upgrade my forge&amp;rdquo;&amp;hellip;. but this time you will&amp;hellip; If you are not happy, you can contact the support and get a refund for the previous year and a 100% discount for the next one :D&lt;/p&gt;

&lt;p&gt;Joking aside, you need to replace your plugin URL with a PURL one. &lt;a target=&quot;_blank&quot; href=&quot;https://ecma-international.org/publications-and-standards/standards/ecma-427/&quot;&gt;ECMA PURL&lt;/a&gt; is a specification to produce &amp;ldquo;a standardized URL-based syntax that uniquely identifies software packages&amp;rdquo;.&lt;/p&gt;

&lt;p&gt;This corresponds to the following change:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#f55&quot;&gt;- url: https://gitroot.dev/releases/0.4.0/ladybug-0.0.4.wasm
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#f55&quot;&gt;- checksum: sha256:7409837469ce89d5c9fd8dd48236764650f48523ff4d81295967154977a01635
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#f55&quot;&gt;- name: ladybug
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#f55&quot;&gt;- version: 0.0.4
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#f55&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#50fa7b;font-weight:bold&quot;&gt;+ purl: pkg:gitroot/gitroot/ladybug@0.0.5?checksum=sha256:afb22a26e7d489d72db0a2305d433a3c3ac2692959ba2412011c0d759b4c8518&amp;amp;registry=https:%2F%2Fgitroot.dev%2Freleases%2F0.5.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#50fa7b;font-weight:bold&quot;&gt;&lt;/span&gt;  active: true
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;GitRoot will download &lt;code&gt;$registry/$scope-$name-$version.wasm&lt;/code&gt;, so it will be &lt;code&gt;https://gitroot.dev/releases/0.5.0/gitroot-ladybug-0.0.5.wasm&lt;/code&gt; for the example. And then compare the downloaded WASM binary with the checksum provided in the PURL.&lt;/p&gt;
&lt;blockquote style=&quot;border-color: #be4700&quot;&gt;

&lt;p&gt;Look in the &lt;a href=&quot;plugins/index.html&quot;&gt;plugins registry&lt;/a&gt; to find the exact purl for each plugin.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To know why, how and more &lt;a href=&quot;blog/version-0.5.0.html#purl-breaking-details&quot;&gt;read purl details&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;a-lot-of-people-have-discovered-gitroot&quot;&gt;A lot of people have discovered GitRoot&lt;/h3&gt;

&lt;p&gt;First show, first lobste.rs/HN links, first video&amp;hellip; Yes, more and more people are talking about GitRoot. &lt;a href=&quot;blog/version-0.5.0.html#gitroot-communication&quot;&gt;Discover where&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;not-conclusive&quot;&gt;Not conclusive&lt;/h3&gt;

&lt;p&gt;Sometimes you spend more time on a subject than you thought. And sometimes, after all of this time spent, you don&amp;rsquo;t have what you wanted to have at the start&amp;hellip; That is the case for the &lt;code&gt;sdkgenerator&lt;/code&gt; experiment. It should &amp;ldquo;just&amp;rdquo; generate Rust, TS, Zig code from Golang&amp;hellip; &amp;ldquo;Just&amp;rdquo;, lol, I never finished it. Maybe someone wants help? &lt;a href=&quot;blog/version-0.5.0.html#sdkgenerator-full-story&quot;&gt;Read the full sdkgenerator story&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;before-it-s-too-late&quot;&gt;Before it&amp;rsquo;s too late&lt;/h3&gt;

&lt;p&gt;How to prevent spam with a forge without registration? This question was asked by NLnet 2 years ago (no, they don&amp;rsquo;t fund GitRoot, I was rejected). Two years later I have the answer, not perfect, but it&amp;rsquo;s a start. I added two new properties: one in &lt;code&gt;.gitroot/forgeConf.yml&lt;/code&gt; of your root repository, &lt;code&gt;NbMaxBranchesPerProjectBeforeRejectingAnonymous&lt;/code&gt;, which is the number of branches before rejecting anonymous pushes. And one in &lt;code&gt;.gitroot/repoConfiguration.yml&lt;/code&gt; of all your repositories, &lt;code&gt;NbMaxBranchesBeforeRejectingAnonymous&lt;/code&gt;, which serves the same purpose but for a single project. &lt;a href=&quot;blog/version-0.5.0.html#max-anonymous-branches&quot;&gt;Read why and see the full example&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;my-new-favorite-language-until-the-next-one&quot;&gt;My new favorite language&amp;hellip;. until the next one&lt;/h3&gt;

&lt;p&gt;After Golang, AssemblyScript, and Rust, the plugin-sdk is now accessible for the Zig language. I continue to add new languages and discover how they work. This one was especially refreshing in its implementation. &lt;a href=&quot;blog/version-0.5.0.html#zig-plugin-sdk&quot;&gt;Read more&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;plugins-needs-more-power&quot;&gt;Plugins needs more power&lt;/h3&gt;

&lt;p&gt;Talking about the plugin-sdk, they all receive a lot of new features. They can now &lt;code&gt;schedule&lt;/code&gt; jobs, make &lt;code&gt;http&lt;/code&gt; requests, and configure plugins during &lt;code&gt;CallFunc&lt;/code&gt;. I have also simplified the initialization part. &lt;a href=&quot;blog/version-0.5.0.html#pluginsdk-improvements&quot;&gt;Read details&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;sftp-to-save-the-web&quot;&gt;Sftp to save the web&lt;/h3&gt;

&lt;p&gt;How to add images to the web without cluttering Git? That was the problem I had to tackle before adding slideshows to GitRoot. SFTP came to the rescue, and with ~200 lines of code, it&amp;rsquo;s now possible. I love when a simple, established protocol allows you to fix a complicated feature. &lt;a href=&quot;blog/version-0.5.0.html#sftp-feature&quot;&gt;Read more&lt;/a&gt; to know how to enable it.&lt;/p&gt;

&lt;h3 id=&quot;how-many-bugs&quot;&gt;How many bugs?&lt;/h3&gt;

&lt;p&gt;A lot of bug fixes have been done. Let me explain &lt;a href=&quot;blog/version-0.5.0.html#some-interesting-bug-fixes&quot;&gt;some interesting ones&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;in-depth-details&quot;&gt;In-depth Details&lt;/h2&gt;

&lt;h3 id=&quot;purl-breaking-details&quot;&gt;PURL breaking details&lt;/h3&gt;

&lt;p&gt;At the beginning of this story, I only wanted to add a &lt;a href=&quot;issues/close/5dd3-plugin-scope.html&quot;&gt;scope&lt;/a&gt; for plugins. That way, if you install a plugin with &lt;code&gt;gitroot&lt;/code&gt; as the scope, you can think &amp;ldquo;it looks less dangerous&amp;rdquo; than &lt;code&gt;anyother&lt;/code&gt; scope. Just like I suspect less &lt;code&gt;@angular&lt;/code&gt; than some &lt;code&gt;odd&lt;/code&gt; Node.js package (well, the npm ecosystem is maybe a bad example for that&amp;hellip; but the only one right now).&lt;/p&gt;

&lt;p&gt;So, my simple idea was to transform &lt;code&gt;url: https://gitroot.dev/releases/0.4.0/ladybug-0.0.4.wasm&lt;/code&gt; into something like &lt;code&gt;url: https://gitroot.dev/releases/0.4.0/gitroot-ladybug-0.0.4.wasm&lt;/code&gt;, so that a &lt;code&gt;gitroot-*.wasm&lt;/code&gt; is affiliated with me. In the &lt;a href=&quot;plugins/index.html&quot;&gt;registry&lt;/a&gt;, I can show the scope.&lt;/p&gt;

&lt;p&gt;Another example is if someone forks &lt;code&gt;ladybug&lt;/code&gt; and wants to release it in the &lt;a href=&quot;plugins/index.html&quot;&gt;registry&lt;/a&gt;. Previously there was no option to do that, so adding a scope seemed to be the best choice.&lt;/p&gt;

&lt;p&gt;During my research on a way to model this scope, I found the &lt;a target=&quot;_blank&quot; href=&quot;https://ecma-international.org/publications-and-standards/standards/ecma-427/&quot;&gt;ECMA-427 Package-URL (PURL)&lt;/a&gt; specification. PURL stands for Package URL (I call them Plugin URL), a way to express many packages as a URL. What a perfect fit for the GitRoot plugin system!&lt;/p&gt;

&lt;p&gt;This is how a PURL is represented: &lt;code&gt;scheme:type/namespace/name@version?qualifiers#subpath&lt;/code&gt;. In GitRoot, I use only &lt;code&gt;pkg:gitroot/$scope/$name@$version?checksum=$checksum&amp;amp;registry=$urlToDownload&lt;/code&gt; (maybe a default registry like &lt;code&gt;https:%2F%2F%2Fgitroot.dev&lt;/code&gt; will be added in a next version, I&amp;rsquo;m not sure about that for now). There is no specification about what a registry should do to implement the GitRoot specification. For now, GitRoot will download &lt;code&gt;$urlToDownload/$scope-$name-$version.wasm&lt;/code&gt;, and that&amp;rsquo;s all.&lt;/p&gt;

&lt;p&gt;Thanks to PURL, I can delete a lot of other properties in the &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt; file, like version, checksum, and name. I also use it a lot in the code to refer to a plugin. Previously I only used the name, but now I use a full PURL. So, for example, if a plugin wants to call another plugin (thanks to the &lt;code&gt;CallFunc&lt;/code&gt; API), it can define it as &lt;code&gt;pkg:gitroot/$scope/$name&lt;/code&gt; (to mean all possible versions), or an exact version with &lt;code&gt;pkg:gitroot/$scope/$name@$version&lt;/code&gt;. Adding a checksum and even a registry is also possible.&lt;/p&gt;

&lt;p&gt;That was a big refactor to do, but I&amp;rsquo;m pretty happy with the result.&lt;/p&gt;

&lt;h3 id=&quot;gitroot-communication&quot;&gt;GitRoot communication&lt;/h3&gt;

&lt;p&gt;A lot has occured during this summer, on the communication side too.&lt;/p&gt;

&lt;p&gt;I want to thank &lt;a target=&quot;_blank&quot; href=&quot;https://adullact.org/&quot;&gt;Adullact&lt;/a&gt; and particularly Matthieu for letting me present the first show at the &lt;a target=&quot;_blank&quot; href=&quot;https://adullact.org/congres&quot;&gt;Adullact congress&lt;/a&gt;. The video is only in French without subtitles: &lt;a target=&quot;_blank&quot; href=&quot;https://video.adullact.org/w/kYxpgoVZfoPcLsDe19jvQw&quot;&gt;Souveraineté : Savez-vous vraiment qui contrôle vos logiciels ?&lt;/a&gt;. It was a great two-day congress, I talked to a lot of passionate people. And for once, they weren&amp;rsquo;t developers.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://gitroot.dev/branches/v0.5/blog/version-0.5.0-cover.png&quot; alt=&quot;Chart with number of unique visitor, a uge pick (from 500 to 13k) is visible around end of jul start of aout.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Then the first appearance on &lt;a target=&quot;_blank&quot; href=&quot;https://lobste.rs/s/z01edy&quot;&gt;lobste.rs&lt;/a&gt; and &lt;a target=&quot;_blank&quot; href=&quot;https://news.ycombinator.com&quot;&gt;Hacker News&lt;/a&gt; occurred. Thanks &lt;a target=&quot;_blank&quot; href=&quot;https://fosstodon.org/@hasnep&quot;&gt;hasnep&lt;/a&gt; for sharing, you can follow the initial exchange on my &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge/statuses/01KXTPKHP5KVWHETKSMC24B32E&quot;&gt;GoToSocial&lt;/a&gt;. As the image shows, there was a spike in the number of visitors, emails, and questions asked on the platforms and in private. That was a great moment to experience.&lt;/p&gt;

&lt;p&gt;After the mentions on these platforms, some other cool articles popped up (I won&amp;rsquo;t link them as they are principally AI-generated slop). But there was also a video showing how to run GitRoot in a &lt;a target=&quot;_blank&quot; href=&quot;https://nanos.org/&quot;&gt;nanoVM&lt;/a&gt;: &lt;a target=&quot;_blank&quot; href=&quot;https://www.youtube.com/watch?v=g5nW9g5T4wU&quot;&gt;https://www.youtube.com/watch?v=g5nW9g5T4wU&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another particular point of pride was the first mention by &lt;a target=&quot;_blank&quot; href=&quot;https://nesbitt.io/&quot;&gt;Andrew Nesbitt&lt;/a&gt; in the blog article &lt;a target=&quot;_blank&quot; href=&quot;https://nesbitt.io/2026/08/20/issues-in-the-repo.html&quot;&gt;Issues in the Repo&lt;/a&gt;. Thanks to Damien for talking about GitRoot to Andrew. Since you deleted the toot, I guess you don&amp;rsquo;t want to be cited here. If that&amp;rsquo;s not the case, contact me.&lt;/p&gt;

&lt;p&gt;And GitRoot has been added to &lt;a target=&quot;_blank&quot; href=&quot;https://alternativeto.net/software/gitroot/about/&quot;&gt;AlternativeTo&lt;/a&gt;. I don&amp;rsquo;t know if this will lead to anything, but hey, I&amp;rsquo;m right alongside GitHub and GitLab :P&lt;/p&gt;

&lt;h3 id=&quot;sdkgenerator-full-story&quot;&gt;SdkGenerator full story&lt;/h3&gt;

&lt;p&gt;This experiment comes from a bored developer who didn&amp;rsquo;t want to use LLMs, but also didn&amp;rsquo;t want to rewrite the same models/objects/structs (call that what you want) four times. When I want to add a new function to the plugins-sdk, I do it in one language, and then, when it works, I need to rewrite it in all the other languages (more on that in the &amp;ldquo;&lt;a href=&quot;blog/modifying-API-SDK-plugin.html&quot;&gt;Modifying the API in the SDK Plugin&lt;/a&gt;&amp;rdquo; blog article).&lt;/p&gt;

&lt;p&gt;In my day job, I love to build tools that generate code. I have written a tool that takes Kotlin as input and generates TypeScript output. Then I wrote a tool that generates complete Angular components from Kotlin enums (yeah, bored of writing a form select from an enum). And finally, it generates complex Angular services from Spring annotations (when you are very bored, a lot of things can be generated).&lt;/p&gt;

&lt;p&gt;So it&amp;rsquo;s naturally that I started to write a tool that generates TS, Rust, and Zig from Go. But the experience is not conclusive&amp;hellip; A lot of language specifics cannot easily be shared by a tool. I&amp;rsquo;m not giving up&amp;hellip; I just have more interesting priorities (yes, I&amp;rsquo;m less bored on GitRoot than at my work ^^). But if this is the kind of thing you like, yes, you, reading that, please reach out to me, and I&amp;rsquo;ll explain all that I did and how you can take it further.&lt;/p&gt;

&lt;p&gt;By the way, I discovered another kind of tool that does a similar job: &lt;a target=&quot;_blank&quot; href=&quot;https://component-model.bytecodealliance.org/design/wit.html&quot;&gt;WIT&lt;/a&gt;. It&amp;rsquo;s not an official spec from the W3C, but since it&amp;rsquo;s from the Bytecode Alliance, it&amp;rsquo;s big enough to use. I have quickly looked at it, and the engine I use for Wasm is not really compatible for now. But I&amp;rsquo;m keeping that in mind; it could be a replacement for &lt;code&gt;SdkGenerator&lt;/code&gt; if nobody (including me) wants to do the job.&lt;/p&gt;

&lt;h3 id=&quot;max-anonymous-branches&quot;&gt;Max anonymous branches&lt;/h3&gt;

&lt;p&gt;When a lot of people started to talk about GitRoot, one thing haunted my nights: what if someone builds a simple script that clones GitRoot, creates 1,000 branches, and pushes them? I would need to &lt;code&gt;git push origin :branch&lt;/code&gt; for every single one of them. You don&amp;rsquo;t need to register to contribute to GitRoot, and that&amp;rsquo;s one of its strengths, but also one of its weaknesses.&lt;/p&gt;

&lt;p&gt;How to make that impossible without blocking all the beauty of GitRoot? One day, a simple solution came to me (yes, I love imagining scenarios running against me). Why not limit the number of branches from anonymous users? It&amp;rsquo;s simple after all: I need to count the number of branches made by anonymous users, and if it exceeds a limit, I stop accepting them. However, people I have added to my repo because of a previous contribution can still push new branches. That is what I added, but&amp;hellip; there is always a but&amp;hellip; counting the number of branches is lightweight, but counting the number of branches from an anonymous user is really expensive. Life is full of choices, and I made one.&lt;/p&gt;

&lt;p&gt;When the branch limit is reached, we stop accepting anonymous contributions. That means if you set 10 as the maximum and you make 20 branches, nobody else can contribute. Yes, it&amp;rsquo;s not perfect, but it&amp;rsquo;s way better than having nightmares every night.&lt;/p&gt;

&lt;p&gt;If an admin configures the forge with &lt;code&gt;NbMaxBranchesPerProjectBeforeRejectingAnonymous=10&lt;/code&gt;, that means when a project has 10 branches (who added them doesn&amp;rsquo;t matter), the next anonymous user (not present in &lt;code&gt;.gitroot/users.yml&lt;/code&gt;) will be rejected. Of course, all known users can continue to create as many branches as they want.&lt;/p&gt;

&lt;p&gt;A project can set a lower number, &lt;code&gt;NbMaxBranchesBeforeRejectingAnonymous=5&lt;/code&gt;, but if the number is bigger, it will have no effect (e.g., if set to &lt;code&gt;NbMaxBranchesBeforeRejectingAnonymous=15&lt;/code&gt;, GitRoot will use 10 as set by the forge administrator).&lt;/p&gt;

&lt;p&gt;A neat trick: setting &lt;code&gt;NbMaxBranchesBeforeRejectingAnonymous=0&lt;/code&gt; blocks all anonymous pushes. Alternatively, you can use the &lt;a href=&quot;doc/how-tos/no_anonymous_user.html&quot;&gt;Not accepting anonymous contribution&lt;/a&gt; guide. The main difference is the error message: the first gives &lt;code&gt;anonymous branches not allowed&lt;/code&gt;, while the latter shows &lt;code&gt;you can&#39;t write in $branchName&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;zig-plugin-sdk&quot;&gt;Zig plugin-sdk&lt;/h3&gt;

&lt;p&gt;With every release, I try to add a new plugin-sdk language. This time I picked &lt;a target=&quot;_blank&quot; href=&quot;https://ziglang.org/&quot;&gt;zig&lt;/a&gt; because it is a language that natively manages Wasm and WASI, so I was pretty sure I would finish this implementation.&lt;/p&gt;

&lt;p&gt;I loved the error system, compared to Rust (optionals) and Go (multiple returns), Zig chose an in-between solution with &lt;code&gt;!&lt;/code&gt; and some language keywords. But what totally blew my mind is the &lt;code&gt;comptime&lt;/code&gt; feature, where the compiler can become an interpreter for compilation output. Amazing idea, I am pretty sure a lot of things will follow this path.&lt;/p&gt;

&lt;p&gt;Long story short, I spent a long time discovering the language, and it was a very good time for me (it changes your perspective, seeing the same problem solved another way)&amp;hellip; but nothing related directly to GitRoot.&lt;/p&gt;

&lt;p&gt;As with all the plugin-sdks, there is unfortunately no way for you to use it yet. I don&amp;rsquo;t release them in registries until the API is stable, and a lot of breaking changes are coming in the next release.&lt;/p&gt;

&lt;h3 id=&quot;pluginsdk-improvements&quot;&gt;PluginSdk improvements&lt;/h3&gt;

&lt;p&gt;Regarding the plugin-sdk, here are a lot of new capabilities for plugins to use:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;schedule&lt;/code&gt;: you can allow a plugin to be called at a scheduled time. The format is a cron expression with 5 segments as &lt;code&gt;&amp;lt;minute&amp;gt; &amp;lt;hour&amp;gt; &amp;lt;day&amp;gt; &amp;lt;month&amp;gt; &amp;lt;weekday&amp;gt;&lt;/code&gt;. Example:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;purl&lt;/span&gt;: pkg:gitroot/gitroot/firefly@0.0.1?checksum=sha256:fae956f5cc9239a87b1c2a149a5466fec85476377de4ada33dda190edb3700c4&amp;amp;registry=https:%2F%2Fgitroot.dev%2Freleases%2F0.5.0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;active&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;schedule&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;* * * * *&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That means the firefly plugin will be executed on the main branch every minute. Refer to the &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/adhocore/gronx#cron-expression&quot;&gt;gronx library documentation&lt;/a&gt; for cron expression details.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;httpClient&lt;/code&gt;: plugins can make HTTP requests. I have added standard verbs (&lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt; and &lt;code&gt;DELETE&lt;/code&gt;), if you need more, please let me know. You can also add headers as a &lt;code&gt;map[string][]string&lt;/code&gt;. In the next version, I will add permission a plugin must request beforehand.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;callFunc&lt;/code&gt; configuration: you can add a configuration for the called plugin within the caller plugin&amp;rsquo;s &lt;code&gt;callFunc&lt;/code&gt;. This makes it possible to tell the called plugin what its configuration is in this context. Previously, an empty config was set, so the plugin&amp;rsquo;s defaults (if any) were used. Example:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;callFunc&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;pluginPURL&lt;/span&gt;: pkg:gitroot/gitroot/apex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;funcName&lt;/span&gt;: renderHtml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;branchesDir&lt;/span&gt;: branches
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;favicon&lt;/span&gt;: logo.png
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;footer&lt;/span&gt;: &amp;lt;p&amp;gt;Start with &amp;lt;code&amp;gt;git clone ssh://gitroot.dev/&amp;lt;/code&amp;gt; 🚀&amp;lt;/p&amp;gt;&amp;lt;small&amp;gt;Hosted with ❤️ by Gitroot&amp;lt;/small&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;generateGitWorktree&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt;9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;header&lt;/span&gt;: ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;init&lt;/code&gt; function, one to rule them all: I have refactored the &lt;code&gt;init()&lt;/code&gt; function of the plugin-sdk to provide an &lt;code&gt;initKind model.InitKind&lt;/code&gt;, which can be &lt;code&gt;confhaschange&lt;/code&gt;, &lt;code&gt;diff&lt;/code&gt;, &lt;code&gt;schedule&lt;/code&gt;, or &lt;code&gt;funccall&lt;/code&gt;. This lets the plugin know in which context it is running, and this architecture allows future hooks to be added without breaking the API.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pull-iterator&lt;/code&gt;: I have introduced pull-iterators to be able to group reports without sending all reports to the Wasm memory. In a future version, I will use this mechanism to transfer commits and files directly to Wasm.&lt;/p&gt;

&lt;h3 id=&quot;sftp-feature&quot;&gt;Sftp feature&lt;/h3&gt;

&lt;p&gt;Users can access every repository&amp;rsquo;s web directory through SFTP. To do this, you can use the same SSH key that you use for your Git repository. When you log in with &lt;code&gt;sftp -P $SshPort $SshAddress:/$repo/&lt;/code&gt; (like &lt;code&gt;sftp -P 4545 127.0.0.1:/repo1/&lt;/code&gt;), you will see the list of web representations for your repository. Anyone can do this, but only those in the &amp;ldquo;owner&amp;rdquo; section of &lt;code&gt;.gitroot/users.yml&lt;/code&gt; have write access.&lt;/p&gt;
&lt;blockquote style=&quot;border-color: #006ac7&quot;&gt;

&lt;p&gt;Be careful: this feature is in alpha, so don&amp;rsquo;t write scripts around it for now. I will probably change the directory hierarchy &lt;a href=&quot;issues/d42a-sftp-better-tree.html&quot;&gt;#d42a&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To enable this feature, you need to change &lt;code&gt;activesftp: false&lt;/code&gt; to &lt;code&gt;activesftp: true&lt;/code&gt; in &lt;code&gt;.gitroot/forgeConfig.yml&lt;/code&gt; at the root repository of your instance. Don&amp;rsquo;t forget to restart GitRoot after that, otherwise the parameter will not be taken into account by GitRoot.&lt;/p&gt;

&lt;h3 id=&quot;some-interesting-bug-fixes&quot;&gt;Some interesting bug fixes&lt;/h3&gt;

&lt;p&gt;Of course, a lot of bug fixes have been included in this release. I will present only the most interesting ones, and you can find a more complete list in the &lt;a href=&quot;CHANGELOG.html&quot;&gt;changelog&lt;/a&gt; and an even more thorough one in the commits.&lt;/p&gt;

&lt;p&gt;The first annoying bug occurred when &lt;a href=&quot;plugins/name/grafter.html&quot;&gt;Grafter&lt;/a&gt; committed to a file, but &lt;a href=&quot;plugins/name/apex.html&quot;&gt;Apex&lt;/a&gt; did not render that file if the Grafter plugin was placed after Apex in the configuration. In fact, even if you put Grafter before Apex, the bug persisted because Apex &lt;code&gt;reports&lt;/code&gt; the rendering back to Grafter, meaning it missed that final report. Now, I have added a subsequent loop where plugins can only write to the web directory (and cache) but not to Git. This way, all plugins loop over commits and files to generate new commits, and only afterwards do we loop through all plugins to see if they have anything to write to the web (or cache) excluding Git to avoid an infinite loop.&lt;/p&gt;

&lt;p&gt;Another annoying issue happened when you changed a plugin&amp;rsquo;s configuration, causing it to enter a worktree loop (looping over files in the worktree instead of commits). Consequently, the commits generated by this loop were not propagated to other plugins, causing widespread rendering misses on the web.&lt;/p&gt;

&lt;p&gt;Finally, if you decided to add a plugin multiple times in your &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt; file (to have multiple configurations, for instance), only the first instance was actually loaded and executed. Now, you can include a plugin as many times as you want, with any configuration you choose, and they will all be executed.&lt;/p&gt;

&lt;h2 id=&quot;what-next&quot;&gt;What next?&lt;/h2&gt;

&lt;p&gt;New plugins, bug fixes, updates&amp;hellip; Yes, until now this article was only about GitRoot, but what is GitRoot without its plugins? The immediate next thing I will do is write an article about what new plugin versions can do.&lt;/p&gt;

&lt;p&gt;Then I will probably start working on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discover GitRoot without installing anything.&lt;/li&gt;
&lt;li&gt;Create your own plugins with ease.&lt;/li&gt;
&lt;li&gt;Transform GitRoot into a registry for (at least) Go, Rust, NPM and Zig.&lt;/li&gt;
&lt;li&gt;Federation with GitRoot but also other forges.&lt;/li&gt;
&lt;li&gt;Implement something for users (how to represent them).&lt;/li&gt;
&lt;li&gt;Manage more Git functions in plugins (branch, pull, push, rebase).&lt;/li&gt;
&lt;li&gt;Use pull-iterators to stabilise the plugin-sdk.&lt;/li&gt;
&lt;li&gt;Easier configuration of plugins.&lt;/li&gt;
&lt;li&gt;Auto-update of plugins.&lt;/li&gt;
&lt;li&gt;New language plugin-sdk.&lt;/li&gt;
&lt;li&gt;Air-gapped version of GitRoot.&lt;/li&gt;
&lt;li&gt;Make every repository a gemini capsule.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you compare this with the &lt;a href=&quot;blog/version-0.4.0.html#what-next&quot;&gt;last v0.4 what next&lt;/a&gt; section, there are a lot more things. Every time I finish a feature (or don&amp;rsquo;t, see the &lt;a href=&quot;blog/version-0.5.0.html#sdkgenerator-full-story&quot;&gt;plugin-sdk generator&lt;/a&gt;), I can add ten more new features to do. That&amp;rsquo;s why I love GitRoot, and that&amp;rsquo;s why &lt;code&gt;I Move slowly, and take care of things&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Special thanks to &lt;code&gt;Ryuno-Ki&lt;/code&gt;, &lt;code&gt;Matthieu&lt;/code&gt; and &lt;code&gt;ks&lt;/code&gt; from the &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/#gitroot:matrix.org&quot;&gt;#gitroot:matrix.org&lt;/a&gt; Matrix room for reviewing and correcting this article. Want to help with future posts? Join us in the room!&lt;/em&gt;&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.5.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.4.0.md</link><title>0.4.0</title><pubDate>2026/04/22</pubDate><description>&lt;h1 id=&quot;the-teenager-starts-building-its-own-future&quot;&gt;The teenager starts building its own future&lt;/h1&gt;

&lt;h2 id=&quot;if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes&lt;/h2&gt;

&lt;p&gt;A lot has been done in this new release of GitRoot. When I saw other FOSS projects claiming &amp;ldquo;this is the biggest release&amp;rdquo;, I thought it was a joke. But in fact, it&amp;rsquo;s true: every new release gets bigger and bigger. It&amp;rsquo;s just astonishing to start a sprint with almost zero ideas and, 4 months later, see all of this accomplished.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you skim this article, don&amp;rsquo;t forget to check out the conclusion about &lt;a href=&quot;blog/version-0.4.0.html#what-next&quot;&gt;&amp;ldquo;what&amp;rsquo;s next?&amp;rdquo;&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;the-thing-i-m-most-proud-of&quot;&gt;The thing i&amp;rsquo;m most proud of&lt;/h3&gt;

&lt;p&gt;Executors can finally build GitRoot! As I love to say: GitRoot can build GitRoot on GitRoot. And it was a pain to do. &lt;a href=&quot;blog/version-0.4.0.html#executor&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-thing-with-the-most-visibility&quot;&gt;The thing with the most visibility&lt;/h3&gt;

&lt;p&gt;The plugin registry is here! After a lot of reading and reflecting, I started this &amp;ldquo;epic story&amp;rdquo;. Today is only the beginning, but you can now find plugins directly at &lt;a href=&quot;plugins/index.html&quot;&gt;gitroot.dev/plugins&lt;/a&gt;. What&amp;rsquo;s next? &lt;a href=&quot;blog/version-0.4.0.html#plugin-registry&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-thing-that-changes-everything-but-was-so-simple&quot;&gt;The thing that changes everything but was so simple&lt;/h3&gt;

&lt;p&gt;Previously, a branch (and graft) couldn&amp;rsquo;t be rendered on the web. The problem was: if I have an &lt;code&gt;index.md&lt;/code&gt; in my git that renders at &lt;code&gt;myinstance.com/index.html&lt;/code&gt;, anyone can create a branch. If a &amp;ldquo;bad user&amp;rdquo; comes along, changes everything in this file, creates a branch, and pushes for review, where should this file render? Certainly not at &lt;code&gt;myinstance.com/index.html&lt;/code&gt;. &lt;a href=&quot;blog/version-0.4.0.html#apex-render-branch&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-thing-that-was-more-complex-than-i-thought&quot;&gt;The thing that was more complex than I thought&lt;/h3&gt;

&lt;p&gt;Apex was a big plugin. Basically, it manages the rendering of your git files to the web. But over time, it started to grow out of control because of markdown, code, worktrees, emojis&amp;hellip; A simple (in theory) solution was to split it into multiple plugins. &lt;a href=&quot;blog/version-0.4.0.html#plugin-deps&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-things-i-m-just-happy-are-done&quot;&gt;The things I&amp;rsquo;m just happy are done&lt;/h3&gt;

&lt;p&gt;No more mandatory signed commits! Well, it&amp;rsquo;s still better if you sign them, but this change means the &lt;code&gt;.gitroot/init.sh&lt;/code&gt; has been &lt;del&gt;deleted&lt;/del&gt; moved to a plugin. &lt;a href=&quot;blog/version-0.4.0.html#sigma-plugin&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Moving a file from git to web was hard, deleting a file was &amp;ldquo;not implemented&amp;rdquo;, and a lot of APIs were missing in the plugin-sdk. I got my hands dirty with all that, and now everything is running smoothly. &lt;a href=&quot;blog/version-0.4.0.html#fs-plugin-sdk&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;in-depth-details&quot;&gt;In-depth Details&lt;/h2&gt;

&lt;h3 id=&quot;executor&quot;&gt;Executor&lt;/h3&gt;

&lt;p&gt;When I started this feature, I just wanted a lightweight container to run &lt;code&gt;make build&lt;/code&gt; and output the result as a &lt;code&gt;gitroot-next&lt;/code&gt; canary binary. If I had known how much time and energy it would actually cost, I’m not sure I would have taken this road.&lt;/p&gt;

&lt;p&gt;At first, I chose &lt;a href=&quot;doc/technicals/executors.html#bwrap-executor&quot;&gt;bubblewrap&lt;/a&gt; as a lightweight container, and immediately started building a &lt;a href=&quot;doc/technicals/executors.html#container-executor&quot;&gt;container&lt;/a&gt; version. Then I realized I needed a &lt;a href=&quot;doc/technicals/executors.html#baremetal-executor&quot;&gt;bareMetal&lt;/a&gt; one. And then, I needed a way to run it on other machines, so &lt;a href=&quot;doc/technicals/executors.html#ssh-executor&quot;&gt;ssh&lt;/a&gt; came along.&lt;/p&gt;

&lt;p&gt;But when I launched it for the first time, I discovered that having &lt;code&gt;make&lt;/code&gt; isn&amp;rsquo;t enough for GitRoot, I need Golang, Rust, TinyGo, and so on. &amp;ldquo;No problem,&amp;rdquo; I thought, &amp;ldquo;I&amp;rsquo;ll use &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt; to manage that.&amp;rdquo; But running &lt;code&gt;mise install&lt;/code&gt; takes a lot of time (like 10-15 minutes on my computer). So, I built a cache system to ensure that hit only happens the first time.&lt;/p&gt;

&lt;p&gt;Still not the end! After that, I needed to configure port numbers, so I built a complete environment variable system, and used it to configure some &lt;code&gt;mise&lt;/code&gt; paths.&lt;/p&gt;

&lt;p&gt;Is it finished? Not yet! To be able to move a binary generated from a bwrap environment to the web, it has to travel a lot. And by &amp;ldquo;a lot,&amp;rdquo; I mean moving it to a place where the plugin that launched the executor can access it: its own cache. Then the plugin needs to move it from there to the web. No problem, I just added a bunch of &lt;a href=&quot;blog/modifying-API-SDK-plugin.html&quot;&gt;SDK API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After all that, some commands finally started working&amp;hellip; well, kind of. When I ran a bwrap or a container command, I couldn&amp;rsquo;t get the stats of what was happening inside. Having process stats is something I’m proud of (even if it’s still evolving in future versions), but seeing that bwrap reported 20 MB of RAM while the sub-process was actually consuming 80 MB was very frustrating. Furthermore, I needed a way to extract stats from the remote SSH executor.&lt;/p&gt;

&lt;p&gt;I tried a lot of things, but ultimately, I chose to build an &lt;a href=&quot;app/executor/main.go&quot;&gt;executor CLI&lt;/a&gt; to handle it. I now inject that binary inside your bwrap, container, or SSH environment. This little CLI launches commands, gathers system stats, command status, and logs. It packs everything into a well-formatted JSON and returns it to the executor in GitRoot. GitRoot then manages artifacts/caches and hands the result back to the calling plugin (probably &lt;a href=&quot;plugins/name/hop.html&quot;&gt;hop&lt;/a&gt;), which tells you what’s happening. In this case, it reports back so the &lt;a href=&quot;plugins/name/grafter.html&quot;&gt;grafter plugin&lt;/a&gt; can include the info in the current graft.&lt;/p&gt;
&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;295.9538&quot; height=&quot;429.0792&quot; viewBox=&quot;0 0 295.9538 429.0792&quot;&gt;&lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;295.9538&quot; height=&quot;429.0792&quot; fill=&quot;transparent&quot;/&gt;&lt;defs&gt;&lt;marker id=&quot;arrow-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 10 5 L 0 10 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-start-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;4.5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 5 L 10 10 L 10 0 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;path id=&quot;edge-0&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-0&quot; d=&quot;M 210.589,59.000 L 210.589,107.236&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot; x=&quot;105.97&quot; y=&quot;62.00&quot; width=&quot;90.62&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;151.28&quot; y=&quot;79.70&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;151.28&quot; dy=&quot;0.00&quot;&gt;Exec cmds&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-1&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-1&quot; d=&quot;M 210.679,158.236 L 210.679,206.472&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;path id=&quot;edge-2&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-2&quot; d=&quot;M 210.835,257.472 L 210.835,305.708&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-2&quot; data-label-kind=&quot;center&quot; x=&quot;17.60&quot; y=&quot;260.47&quot; width=&quot;179.24&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-2&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;107.22&quot; y=&quot;278.17&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;107.22&quot; dy=&quot;0.00&quot;&gt;bwrap/container/ssh&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-3&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-3&quot; d=&quot;M 210.955,356.708 L 210.955,370.079&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;path id=&quot;edge-4&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-4&quot; d=&quot;M 166.834,395.579 L 153.002,395.579 L 120.042,347.208 L 133.874,347.208&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;path id=&quot;edge-5&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-5&quot; d=&quot;M 133.874,315.208 L 8.000,315.208 L 8.000,247.972 L 165.077,247.972&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-5&quot; data-label-kind=&quot;center&quot; x=&quot;46.42&quot; y=&quot;258.31&quot; width=&quot;206.12&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-5&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;149.48&quot; y=&quot;276.01&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;149.48&quot; dy=&quot;0.00&quot;&gt;status/code/logs/stats&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-6&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-6&quot; d=&quot;M 165.077,215.972 L 41.355,215.972 L 41.355,148.736 L 153.161,148.736&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-6&quot; data-label-kind=&quot;center&quot; x=&quot;56.06&quot; y=&quot;159.07&quot; width=&quot;206.12&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-6&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;159.12&quot; y=&quot;176.77&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;159.12&quot; dy=&quot;0.00&quot;&gt;status/code/logs/stats&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-7&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-7&quot; d=&quot;M 153.161,116.736 L 26.706,116.736 L 26.706,33.500 L 167.201,33.500&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-7&quot; data-label-kind=&quot;center&quot; x=&quot;57.12&quot; y=&quot;59.83&quot; width=&quot;206.12&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-7&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;160.18&quot; y=&quot;77.53&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;160.18&quot; dy=&quot;0.00&quot;&gt;status/code/logs/stats&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;rect x=&quot;167.20&quot; y=&quot;8.00&quot; width=&quot;86.75&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.58&quot; y=&quot;37.00&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.58&quot; dy=&quot;0.00&quot;&gt;Hop&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;153.16&quot; y=&quot;107.24&quot; width=&quot;114.88&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.60&quot; y=&quot;136.24&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.60&quot; dy=&quot;0.00&quot;&gt;GitRoot&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;133.87&quot; y=&quot;305.71&quot; width=&quot;154.08&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.91&quot; y=&quot;334.71&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.91&quot; dy=&quot;0.00&quot;&gt;executor cli&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;166.83&quot; y=&quot;370.08&quot; width=&quot;88.32&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;211.00&quot; y=&quot;399.08&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;211.00&quot; dy=&quot;0.00&quot;&gt;cmd&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;165.08&quot; y=&quot;206.47&quot; width=&quot;91.36&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.76&quot; y=&quot;235.47&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.76&quot; dy=&quot;0.00&quot;&gt;exec&lt;/tspan&gt;&lt;/text&gt;&lt;g transform=&quot;translate(210.59 107.24) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(210.68 206.47) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(210.84 305.71) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(210.95 370.08) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(133.87 347.21) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(165.08 247.97) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(153.16 148.74) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(167.20 33.50) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;/svg&gt;
&lt;p&gt;Phew, is that enough now? It should be, but we&amp;rsquo;ll see how it performs in real-time on &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev&quot;&gt;gitroot.dev&lt;/a&gt;. Not everything is finished: for example, the SSH executor doesn’t manage cache yet &lt;a href=&quot;issues/94af-executor-cache.html&quot;&gt;#94af&lt;/a&gt; and can&amp;rsquo;t use the container strategy &lt;a href=&quot;issues/f050-ssh-executor-subCmd.html&quot;&gt;#f050&lt;/a&gt;. You can’t run the executor on other architectures &lt;a href=&quot;issues/4d49-executor-compile-other-archs.html&quot;&gt;#4d49&lt;/a&gt; yet, and I’m sure you’ll soon want a way to clean up temp directories &lt;a href=&quot;issues/647f-clean-executor-temp-dir.html&quot;&gt;#647f&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But hey, let’s celebrate what we’ve built before jumping into the next sprint!&lt;/p&gt;

&lt;h3 id=&quot;plugin-registry&quot;&gt;Plugin registry&lt;/h3&gt;

&lt;p&gt;Until now, explaining GitRoot was always about &amp;ldquo;data in git and the plugin system&amp;rdquo;, but what exactly are the plugins? Where are they? What can they do? All of that was a bit mysterious. With this milestone, I’m answering some of those questions with a first shot at a registry. Be careful, though: this first version is just a &amp;ldquo;hand-written&amp;rdquo; preview of what it could be in the future. If you look at the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/plugins/index.md&quot;&gt;source&lt;/a&gt;, you’ll see that everything is ready to be automated, but nothing is&amp;hellip; yet.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;m not entirely sure what you, as a consumer, want to see here. Probably an explanation of what the plugin does, some docs, and the available versions. But as a plugin creator, you likely want to manage all of that in your own repository instead of the &lt;a href=&quot;plugins/index.html&quot;&gt;gitroot.dev registry&lt;/a&gt;. I chose to keep the registry very light and put the details directly in the &lt;a href=&quot;app/plugins/apex/index.html&quot;&gt;plugin&amp;rsquo;s directory&lt;/a&gt; to give you an idea of what your own repo could look like.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ll need to iterate on this. Even if I choose to embed package details in the registry, I still need to decide how: maybe via a JSON in your repo, maybe in the registry itself, or perhaps inside your package (WASM)? I don&amp;rsquo;t know yet what’s simplest for you and for the registry.&lt;/p&gt;

&lt;p&gt;Anyway, I hope this registry helps you understand what a plugin is and how it shapes your forge. Some are small (&lt;a href=&quot;plugins/name/ladybug.html&quot;&gt;ladybug&lt;/a&gt;), others are massive (&lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex&lt;/a&gt;), and some aren&amp;rsquo;t usable solo as they are &lt;a href=&quot;blog/version-0.4.0.html#plugin-deps&quot;&gt;dependencies&lt;/a&gt; for others (like &lt;a href=&quot;plugins/name/apex_mermaid.html&quot;&gt;apx_mermaid&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Since plugins are now decoupled from GitRoot, I’ve moved all changelogs from the &lt;a href=&quot;CHANGELOG.html&quot;&gt;general&lt;/a&gt; one to the individual plugin directories. This way, I’ll be able to release plugin versions independently of GitRoot (and vice versa). I hope to break the plugin API less often, even if I know I probably will in the near future. All of this paves the way for you to build your own plugins and I hope to see them in the gitroot.dev registry soon. Or, of course, you can just build your own registry!&lt;/p&gt;

&lt;h3 id=&quot;apex-render-branch&quot;&gt;Apex render branch&lt;/h3&gt;

&lt;p&gt;For as long as I can remember, this has been a problem with GitRoot: how to render arbitrary changes, especially deletions, on the web? I had imagined a lot of strategies. The last one was to render inside a directory named after the git tree ID (the abstract representation of a working directory). Then, I’d create a symlink between that tree and a main directory.&lt;/p&gt;

&lt;p&gt;The idea was: if you push a file in a branch, git gives you a tree ID like &lt;code&gt;a654fs&lt;/code&gt;. It would be represented as &lt;code&gt;mydomain.com/myBranch/file -&amp;gt; mydomain.com/tree/a654fs/file&lt;/code&gt;. When you merged that branch, GitRoot would simply symlink the main directory to it: &lt;code&gt;mydomain.com/file -&amp;gt; mydomain.com/tree/a654fs/file&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Elegant, but honestly, a total over-engineered mess. When I actually sat down to code this, I ended up adding just two lines to the &lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex plugin&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; p.currentCommit.Branch &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; p.currentCommit.Branch &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; p.config.defaultBranch {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	path = filepath.&lt;span style=&quot;color:#50fa7b&quot;&gt;Join&lt;/span&gt;(p.config.branchesDir, p.currentCommit.Branch, path)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And&amp;hellip; yeah, that was enough. I&amp;rsquo;ve been overthinking this since the very beginning of GitRoot! Rendering files from branches into a dedicated branches directory&amp;hellip; It&amp;rsquo;s simple. Just like GitRoot should be.&lt;/p&gt;

&lt;p&gt;So, what does it mean in practice? The file is now rendered at &lt;code&gt;myinstance.com/mybranch/index.html&lt;/code&gt; and the graft at &lt;code&gt;myinstance.com/mybranch/grafts/mybranch.html&lt;/code&gt;. How did I not think of something this simple before?&lt;/p&gt;

&lt;h3 id=&quot;plugin-deps&quot;&gt;Plugin deps&lt;/h3&gt;

&lt;p&gt;The catalyst for this development was the &lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex plugin&lt;/a&gt;. At over 3 MB, it was by far the largest plugin. It was packed with configuration, specifically a bunch of booleans to &lt;a href=&quot;issues/8b29-apex-working-tree.html&quot;&gt;toggle features&lt;/a&gt;. Furthermore, I needed to render Markdown in other plugins, like &lt;a href=&quot;plugins/name/pollen.html&quot;&gt;pollen&lt;/a&gt;, to create a proper &lt;a href=&quot;issues/83d8-pollen-content-flux.html&quot;&gt;RSS feed&lt;/a&gt;. It became clear to me: this plugin needed to be split into smaller, more specialized ones.&lt;/p&gt;

&lt;p&gt;So, I started building the &lt;a href=&quot;plugins/name/apex_markdown.html&quot;&gt;apex_markdown plugin&lt;/a&gt;, then &lt;a href=&quot;plugins/name/apex_code.html&quot;&gt;apex_code&lt;/a&gt;, and next week &lt;a href=&quot;plugins/name/apex_mermaid.html&quot;&gt;apex_mermaid&lt;/a&gt;. But what I hadn&amp;rsquo;t fully anticipated was the sheer amount of work required to make it all work together.&lt;/p&gt;

&lt;p&gt;What if a dependency is missing? What if a method is deprecated or removed in a newer version? To handle this, I’ve chosen to make all dependencies entirely optional for now. It’s an easy choice to make today, but I might change my mind tomorrow, after all, there’s a reason most package managers handle version ranges and resolution.&lt;/p&gt;

&lt;p&gt;This new architecture allows for adding tons of features without rebuilding an entire plugin. Imagine adding Org-mode or RST support: you just swap out &lt;a href=&quot;plugins/name/apex_markdown.html&quot;&gt;apex_mardown&lt;/a&gt; instead of touching the whole stack. It also allows other plugins to reuse existing behaviors effortlessly.&lt;/p&gt;

&lt;p&gt;With this in place, I’m not even sure if I’ll keep the &lt;code&gt;report&lt;/code&gt; system, which was an other way of passing information between plugins.&lt;/p&gt;

&lt;h3 id=&quot;sigma-plugin&quot;&gt;Sigma plugin&lt;/h3&gt;

&lt;p&gt;Signed commits should be the default in git today. With SSH signing, it’s easy to do, and everyone should use it because impersonating a name and email is way too easy in git. When I started GitRoot, I made signed commits mandatory before allowing a push. But in fact, making signatures mandatory should be a choice for the instance owner. GitRoot is all about the freedom to choose how your forge works for you.&lt;/p&gt;

&lt;p&gt;Furthermore, a friend told me he preferred signing with PGP and already had his setup ready. So why force him to use a &lt;code&gt;.gitroot/init.sh&lt;/code&gt;? It’s frustrating when you want to test a new forge and you’re forced to run random sh commands, even if it&amp;rsquo;s just to configure your git client.&lt;/p&gt;

&lt;p&gt;With this release, I have completely removed &lt;code&gt;init.sh&lt;/code&gt; from GitRoot itself. But of course, as always, I’ve created a plugin to &amp;ldquo;almost&amp;rdquo; recreate this feature. The only difference is: instead of refusing your commit when you push, the &lt;a href=&quot;plugins/name/stigma.html&quot;&gt;stigma plugin&lt;/a&gt; will simply report unsigned or badly signed commits in your graft.&lt;/p&gt;

&lt;p&gt;Now, you have the choice to accept unsigned commits in your repo. You can even ignore the whole signing topic entirely if you don&amp;rsquo;t install the stigma plugin.&lt;/p&gt;

&lt;p&gt;That said, I’m still convinced that signing commits should remain the default.&lt;/p&gt;

&lt;h3 id=&quot;fs-plugin-sdk&quot;&gt;FS plugin-sdk&lt;/h3&gt;

&lt;p&gt;After finishing all the &lt;a href=&quot;blog/version-0.4.0.html#executor&quot;&gt;executors features&lt;/a&gt;, one thing was still missing: the ability for a plugin to move a file from one path to another. As explained in this &lt;a href=&quot;blog/modifying-API-SDK-plugin.html&quot;&gt;blog post&lt;/a&gt;, even though a plugin sees one single filesystem, GitRoot actually uses three different ones: one for cache (a directory), one for the web (a subdirectory), and a virtual one for the git working directory.&lt;/p&gt;

&lt;p&gt;I was missing several methods in both the Rust and TS SDKs, and the existing API was poorly designed with inconsistent naming. I decided to redesign everything and added a lot of new methods accessible via &lt;code&gt;FS&lt;/code&gt; in the SDK. Now, you can copy, move, delete, replace, and write files in every filesystem. Moving and copying can now be done between different filesystems, making it easy to make a file accessible on the web even if it originated from git or the cache.&lt;/p&gt;

&lt;p&gt;I have marked all old methods as deprecated, and they will be removed in the next version of GitRoot (or more specifically, the plugin-sdk, though they share the same release number for now).&lt;/p&gt;

&lt;h2 id=&quot;what-next&quot;&gt;What next?&lt;/h2&gt;

&lt;p&gt;I have a massive roadmap for GitRoot (in no particular order):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discover GitRoot without installing anything.&lt;/li&gt;
&lt;li&gt;Create your own plugins with ease.&lt;/li&gt;
&lt;li&gt;Transform GitRoot into a registry for (at least) Go, Rust, and NPM.&lt;/li&gt;
&lt;li&gt;Plugins should be usable via PURL.&lt;/li&gt;
&lt;li&gt;Plugins can send/receive HTTP requests.&lt;/li&gt;
&lt;li&gt;Plugins can be scheduled.&lt;/li&gt;
&lt;li&gt;Rebase go-git to take advantage of their performance improvements.&lt;/li&gt;
&lt;li&gt;Generate the plugin SDK automatically.&lt;/li&gt;
&lt;li&gt;Add plugin SDKs for (at least) Zig, Kotlin, and Python.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yeah, I could go on all night. And those are just core GitRoot features. I haven&amp;rsquo;t even mentioned making instances to build projects in hosted environments. I haven&amp;rsquo;t talked about specific plugins (sending a &amp;ldquo;toot&amp;rdquo; on commit, managing secrets with age, translating your app&amp;hellip;). I haven&amp;rsquo;t even touched on federating GitRoot with other GitRoot instances, or with Forgejo, SourceHut, or Radicle.&lt;/p&gt;

&lt;p&gt;All of that is possible, and I’m confident it will be accessible in the future. But &amp;ldquo;when&amp;rdquo;? That’s the real question. If you think you can help in any way (coding, of course, but also management, communication, or just to brainstorm), please &lt;a href=&quot;contact.html&quot;&gt;let me know&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is only the beginning. GitRoot will get better and better with every release.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.4.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.3.0.md</link><title>0.3.0</title><pubDate>2025/12/20</pubDate><description>&lt;h1 id=&quot;from-baby-to-teenager-gitroot-become-a-forge&quot;&gt;From &amp;ldquo;baby to teenager&amp;rdquo; GitRoot become a forge&lt;/h1&gt;

&lt;h2 id=&quot;if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes&lt;/h2&gt;

&lt;p&gt;Previously, GitRoot served as a &lt;strong&gt;Source Code Management (SCM)&lt;/strong&gt; system, allowing you to create Git repositories and manage access permissions. However, GitRoot was never intended to be &lt;em&gt;just&lt;/em&gt; an SCM. With this &lt;strong&gt;v0.3.0&lt;/strong&gt; release, it evolves into a full-fledged &lt;strong&gt;forge&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To activate this new capability on your instance, &lt;a href=&quot;blog/version-0.3.0.html#for-admin-activating-an-executor&quot;&gt;follow the administrator section&lt;/a&gt;. To start using it as a user, &lt;a href=&quot;blog/version-0.3.0.html#for-user-using-the-hop-plugin&quot;&gt;see the user section&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The introduction of &lt;strong&gt;executors&lt;/strong&gt; brings a crucial new feature: the &lt;a href=&quot;blog/version-0.3.0.html#plugin-api-report-and-reporter&quot;&gt;report and reporter plugin API&lt;/a&gt;. This API allows any plugin to provide informational feedback to the user. &lt;strong&gt;Important:&lt;/strong&gt; avoid reporting low-level technical details here (use the &lt;code&gt;log&lt;/code&gt; and &lt;code&gt;logError&lt;/code&gt; APIs for that). The initial implementation uses this feature to display command statistics (CPU, memory, etc.) from the new &lt;code&gt;hop&lt;/code&gt; plugin in a clean table via the &lt;code&gt;grafter&lt;/code&gt; plugin.&lt;/p&gt;

&lt;p&gt;With the new &lt;code&gt;hop&lt;/code&gt; plugin, GitRoot now supports 3 languages (Go, TypeScript, Rust) across 4 compilers (Go, TinyGo, Rust, AssemblyScript). Setting up a development environment for this polyglot stack is complex, and keeping it updated is even harder, unless you have the right tools. For this iteration, I have selected &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt; as the default tool for &lt;a href=&quot;blog/version-0.3.0.html#gitroot-bets-on-mise&quot;&gt;managing the GitRoot dev environment&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Beyond technical features, a new focus has been placed on &lt;a href=&quot;blog/version-0.3.0.html#communication-and-community-building&quot;&gt;communication&lt;/a&gt;. You can now follow daily progress on our new fediverse instance (@forge@gitroot.dev), engage in direct chat on matrix (#gitroot:matrix.org), and read a new blog post detailing concrete ways to contribute today.&lt;/p&gt;

&lt;p&gt;Finally, I discuss &lt;a href=&quot;blog/version-0.3.0.html#apex-a-better-web-experience&quot;&gt;Apex&lt;/a&gt; and many &lt;a href=&quot;blog/version-0.3.0.html#and-others-useful-updates&quot;&gt;other tiny but useful things&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;in-depth-details&quot;&gt;In-depth Details&lt;/h2&gt;

&lt;h3 id=&quot;for-admin-activating-an-executor&quot;&gt;For admin: activating an executor&lt;/h3&gt;

&lt;p&gt;When you update GitRoot to &lt;code&gt;0.3.0&lt;/code&gt; a new configuration block will appear in the &lt;code&gt;.gitroot/forgeConfig.yml&lt;/code&gt; file of your &lt;code&gt;root&lt;/code&gt; repository:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;execconf&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;baremetal&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;bwrap&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;user&lt;/span&gt;: nobody
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;uid&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;65534&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;gid&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;65534&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;robind&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;bind&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;container&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;bin&lt;/span&gt;: podman
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;ssh&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;hosts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;user&lt;/span&gt;: myUSer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;address&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;127.0.0.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;port&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;22&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;publickey&lt;/span&gt;: ssh-ed25519 AAAAC3NzaC1...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can enable multiple executors, but GitRoot will only use the first one enabled, checking them in this order: &lt;code&gt;bareMetal&lt;/code&gt;, &lt;code&gt;bwrap&lt;/code&gt;, &lt;code&gt;container&lt;/code&gt; and then &lt;code&gt;ssh&lt;/code&gt;. If you activate an executor, remember to also add the &lt;code&gt;hop&lt;/code&gt; plugin, as it is the only known plugin designed to utilize them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;bare-metal-simplest-but-most-dangerous&quot;&gt;Bare Metal: simplest but most dangerous&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;bareMetal&lt;/code&gt; executor runs commands directly on the host machine. It requires no external dependencies and should work on any hardware. Be extremely cautious: users of your GitRoot instance will gain the ability to execute arbitrary code on the host.&lt;/p&gt;

&lt;h4 id=&quot;bubblewrap-bwrap-lightweight-sandboxing&quot;&gt;Bubblewrap (bwrap): lightweight sandboxing&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;bwrap&lt;/code&gt; executor introduces a lightweight container (sandbox) between executed commands and the host, similar to the technology used by &lt;a target=&quot;_blank&quot; href=&quot;https://flatpak.org/&quot;&gt;flatpack&lt;/a&gt;. To use it, you must install the &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/containers/bubblewrap&quot;&gt;bubblewrap&lt;/a&gt; application.&lt;/p&gt;

&lt;p&gt;By default, GitRoot configures &lt;code&gt;bwrap&lt;/code&gt; to use the &lt;code&gt;nobody&lt;/code&gt; user for restriction, though this can be changed. You can also define bindings between your host and the commands running inside the container. For example, &lt;code&gt;robind: [&amp;quot;/var/log/caddy/access.log&amp;quot;]&lt;/code&gt; would allow a job to execute &lt;code&gt;cat /var/log/caddy/access.log&lt;/code&gt;.&lt;/p&gt;

&lt;h4 id=&quot;container-heavyweight-but-most-flexible&quot;&gt;Container: heavyweight but most flexible&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;container&lt;/code&gt; executor enables the execution of commands within &lt;a target=&quot;_blank&quot; href=&quot;https://podman.io/&quot;&gt;podman&lt;/a&gt; or &lt;a target=&quot;_blank&quot; href=&quot;https://www.docker.com/&quot;&gt;docker&lt;/a&gt; containers, allowing your users to run any OCI image. The only requirement for the administrator is to install either &lt;a target=&quot;_blank&quot; href=&quot;https://podman.io/docs/installation&quot;&gt;podman&lt;/a&gt; or &lt;a target=&quot;_blank&quot; href=&quot;https://www.docker.com/&quot;&gt;docker&lt;/a&gt;. Remember to change the &lt;code&gt;bin&lt;/code&gt; property to &lt;code&gt;docker&lt;/code&gt; if that is your chosen runtime.&lt;/p&gt;

&lt;p&gt;GitRoot includes a useful feature for containers: if the user provides a path to a &lt;code&gt;Containerfile&lt;/code&gt; or &lt;code&gt;Dockerfile&lt;/code&gt;, GitRoot will automatically build the image before running commands inside it.&lt;/p&gt;

&lt;h4 id=&quot;ssh-remote-execution-alpha&quot;&gt;SSH: remote execution (alpha)&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;ssh&lt;/code&gt; executor allows commands to be executed on a remote host via a standard SSH connection. You simply provide the standard SSH connection details. This executor should be considered the most alpha of all options (and yes, all others are also considered alpha).&lt;/p&gt;

&lt;h3 id=&quot;for-user-using-the-hop-plugin&quot;&gt;For user: using the &lt;code&gt;hop&lt;/code&gt; plugin&lt;/h3&gt;

&lt;p&gt;To execute a build (or command execution), users must activate the &lt;code&gt;hop&lt;/code&gt; plugin and configure its execution rules:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;url&lt;/span&gt;: /home/rmaneschi/projects/gitroot/app/testsuite/../plugins/hop/hop-0.0.1.wasm
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: hop
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;version&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;0.0.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;active&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          - &lt;span style=&quot;color:#ff79c6&quot;&gt;command&lt;/span&gt;: cat
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          &lt;span style=&quot;color:#ff79c6&quot;&gt;build&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          &lt;span style=&quot;color:#ff79c6&quot;&gt;cmds&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            - &lt;span style=&quot;color:#ff79c6&quot;&gt;args&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;                - README.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;              &lt;span style=&quot;color:#ff79c6&quot;&gt;cmd&lt;/span&gt;: cat
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          &lt;span style=&quot;color:#ff79c6&quot;&gt;env&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add all desired commands for the plugin to call in &lt;code&gt;run.write.exec&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then in &lt;code&gt;run.configuration.exec&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;set environment variables via &lt;code&gt;env&lt;/code&gt; (e.g., &lt;code&gt;&amp;quot;MY_ENV=my_val&amp;quot;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;specify the &lt;code&gt;build&lt;/code&gt; property (only for the &lt;code&gt;container&lt;/code&gt; executor) to use an existing image (e.g., &lt;code&gt;docker.io/bash:5.3.3&lt;/code&gt;) or to build one from a file (e.g., &lt;code&gt;/my/path/to/Containerfile&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;define the commands and arguments to execute under &lt;code&gt;cmds&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;plugin-api-report-and-reporter&quot;&gt;Plugin api report and reporter&lt;/h3&gt;

&lt;p&gt;Occasionally, plugins need to communicate important information to the user. For instance, the &lt;code&gt;ladybug&lt;/code&gt; plugin might need to report invalid metadata. In traditional forges, a web interface serves this purpose, but GitRoot operates without a fixed UI.&lt;/p&gt;

&lt;p&gt;Past considerations included sending emails (a solution often disliked by developers) or leveraging external communication methods like RSS via the &lt;code&gt;pollen&lt;/code&gt; plugin (introduced in v0.2.0) or the Fediverse via a Go-to-Social instance. As always, GitRoot aims to provide options rather than dictate a single solution.&lt;/p&gt;

&lt;p&gt;To allow reporting without forcing a communication channel, I&amp;rsquo;ve created two new plugin APIs.&lt;/p&gt;

&lt;p&gt;Report: plugins call this API with a severity level (&lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, or &lt;code&gt;error&lt;/code&gt;) and an array of strings to display. GitRoot stacks all reports for every commit and plugin.&lt;/p&gt;

&lt;p&gt;Reporter API: after reports are collected, GitRoot calls all plugins that implement the &lt;code&gt;Reporter&lt;/code&gt; interface.&lt;/p&gt;

&lt;p&gt;Currently, the &lt;code&gt;grafter&lt;/code&gt; plugin is the only known reporter; it adds the collected reports to the graft output. This two new API opens up a wide range of possibilities for future versions. I have several ideas, but I encourage you to &lt;a href=&quot;contact.html&quot;&gt;share yours&lt;/a&gt;!&lt;/p&gt;

&lt;h3 id=&quot;gitroot-bets-on-mise&quot;&gt;GitRoot bets on mise&lt;/h3&gt;

&lt;p&gt;Managing a diverse development environment that spans Go, TypeScript, and Rust can be a significant hurdle. In this iteration, I have standardized on mise as the official tool for managing the GitRoot dev environment. This choice simplifies installation and ensures all dependencies remain correctly updated for development.&lt;/p&gt;

&lt;p&gt;This choice seems to be a good one, as we welcome a new contributor who has already integrated some common tasks using &lt;code&gt;mise&lt;/code&gt;. Thanks to their efforts, we can now simply run &lt;code&gt;mise run build&lt;/code&gt; to compile GitRoot or &lt;code&gt;mise run test&lt;/code&gt; to execute all tests.&lt;/p&gt;

&lt;h3 id=&quot;communication-and-community-building&quot;&gt;Communication and community building&lt;/h3&gt;

&lt;p&gt;Alongside the technical work for v0.3.0, a focused effort was put into non-technical tasks to improve communication with the community.&lt;/p&gt;

&lt;p&gt;To allow you to follow daily progress and current thoughts, I have set up a &lt;a target=&quot;_blank&quot; href=&quot;https://gotosocial.org/&quot;&gt;GoToSocial&lt;/a&gt; (Mastodon) instance. You can find the official account at &lt;code&gt;@forge@gitroot.dev&lt;/code&gt; or directly at &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev&quot;&gt;https://gts.gitroot.dev&lt;/a&gt;. This is the best place for real-time updates.&lt;/p&gt;

&lt;p&gt;I also established a direct chat room on &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.org/&quot;&gt;matrix&lt;/a&gt;, accessible via &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/#gitroot:matrix.org&quot;&gt;https://matrix.to/#/#gitroot:matrix.org&lt;/a&gt;. Feel free to stop by to ask questions or just say hello.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ve added a &lt;a href=&quot;contact.html&quot;&gt;contact&lt;/a&gt; page to ensure you can easily find a way to reach me. It includes multiple options such as email, mastodon, or matrix for direct chat. Please don&amp;rsquo;t hesitate to come say &amp;ldquo;hi&amp;rdquo;!&lt;/p&gt;

&lt;p&gt;Finally, I published a blog post titled &lt;a href=&quot;blog/how-to-help_en_11-2025.html&quot;&gt;&amp;ldquo;How to help building GitRoot 11-2025&amp;rdquo;&lt;/a&gt;. The purpose of this post is to present you with concrete, immediate contribution opportunities, rather than speculating on future possibilities. If you wish to get involved, this is the place to start.&lt;/p&gt;

&lt;h3 id=&quot;apex-a-better-web-experience&quot;&gt;Apex a better web experience&lt;/h3&gt;

&lt;p&gt;The Apex plugin receives numerous bug fixes and quality of life improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;favicon support: a favicon can now be added via the configuration&lt;/li&gt;
&lt;li&gt;styled 404 page: the 404 error page now correctly uses the site&amp;rsquo;s CSS&lt;/li&gt;
&lt;li&gt;HTML metadata configuration: I have added configuration options for HTML metadata, which is useful for SEO and for including specific tags like &lt;code&gt;og&lt;/code&gt; (Open Graph) or &lt;code&gt;fediverse&lt;/code&gt; metadata&lt;/li&gt;
&lt;li&gt;menu highlighting: the current page in the navigation menu is now highlighted&lt;/li&gt;
&lt;li&gt;lightweight template: added some variables in header and footer templates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Breaking:&lt;/strong&gt; previously, the &lt;code&gt;apex&lt;/code&gt; plugin rendered markdown metadata as small blocks at the top of the page by default. With this version, it will no longer render this metadata unless the markdown page explicitly includes &lt;code&gt;apexRenderMetadatas: true&lt;/code&gt; in its front matter.&lt;/p&gt;

&lt;h3 id=&quot;and-others-useful-updates&quot;&gt;And others useful updates&lt;/h3&gt;

&lt;p&gt;Although this article is already quite long, I want to briefly mention several smaller yet important tasks completed in this release.&lt;/p&gt;

&lt;h4 id=&quot;forgeconf-update&quot;&gt;ForgeConf update&lt;/h4&gt;

&lt;p&gt;The parameters &lt;code&gt;externalsshaddr&lt;/code&gt; (default value &lt;code&gt;ssh://localhost:4545/&lt;/code&gt;) and &lt;code&gt;externalhttpaddr&lt;/code&gt; (default value &lt;code&gt;http://localhost:4546/&lt;/code&gt;) have been added to ensure the display of correct external urls to users. These addresses are notably used by the &lt;code&gt;apex&lt;/code&gt; plugin generate proper public links. They are kept separate from the internal &lt;code&gt;sshaddr&lt;/code&gt; and &lt;code&gt;httpaddr&lt;/code&gt; because your GitRoot instance might be configured to listen internally (on &lt;code&gt;127.0.0.1&lt;/code&gt;) while being reachable externally through a reverse proxy.&lt;/p&gt;

&lt;h4 id=&quot;integrated-updater&quot;&gt;Integrated updater&lt;/h4&gt;

&lt;p&gt;I&amp;rsquo;ve added a preliminary implementation for an integrated updater. This feature will handle breaking changes gracefully. For this release, I&amp;rsquo;ve added some new parameters to &lt;code&gt;forgeConf.yml&lt;/code&gt;, the integrated updater will handle these by setting standard values that should not affect your current user instance setup.&lt;/p&gt;

&lt;h4 id=&quot;rust-and-wasm&quot;&gt;Rust and WASM&lt;/h4&gt;

&lt;p&gt;While integrating Rust, I discovered that the WASM (and specifically WASI Preview 1) target does not permit exporting the standard &lt;code&gt;free&lt;/code&gt; and &lt;code&gt;malloc&lt;/code&gt; functions, which GitRoot requires. I&amp;rsquo;ve added an option to rename these functions to &lt;code&gt;gitrootAlloc&lt;/code&gt; and &lt;code&gt;gitrootFree&lt;/code&gt;, resolving this compatibility issue.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.3.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.2.0.md</link><title>0.2.0</title><pubDate>2025/09/18</pubDate><description>&lt;h1 id=&quot;version-0-2-0-is-here&quot;&gt;Version 0.2.0 is here&lt;/h1&gt;

&lt;p&gt;In this version, my main focus was on stabilization. I fixed over 14 issues, some of which were identified before version 0.1.0, but a significant proportion were reported by YOU. A big thanks to everyone who took part of it in any way.&lt;/p&gt;

&lt;p&gt;Every time I shared gitroot, I came across new issues with the documentation and binaries, but also questions such as &amp;ldquo;how can I compile it?&amp;rdquo; or &amp;ldquo;what&amp;rsquo;s the next step?&amp;rdquo;. And I&amp;rsquo;m delighted to see your enthusiasm for it!&lt;/p&gt;

&lt;h2 id=&quot;first-contribution&quot;&gt;First contribution&lt;/h2&gt;

&lt;p&gt;Although all my new contributions were related to &amp;ldquo;bug fixes&amp;rdquo;, some of them required new features.&lt;/p&gt;

&lt;h3 id=&quot;no-notification-no-contribution&quot;&gt;No notification == no contribution&lt;/h3&gt;

&lt;p&gt;The first amusing problem came from the first contributor! Fifteen days later, he contacted me to say, &amp;ldquo;Hi, I pushed a branch where I corrected a lot of typos, but&amp;hellip; nothing has happened since I pushed it?&amp;rdquo;. In fact, I wasn&amp;rsquo;t actually aware that a new branch had been pushed. I had forgotten to implement a mechanism that would notify me whenever a new contribution is published!&lt;/p&gt;

&lt;p&gt;A simple way to achieve this is to add an RSS feed (yes, I was inspired by Radicle for this feature). This is ideal, because all I have to do is add an &amp;ldquo;rss.xml&amp;rdquo; file to the web folder&amp;hellip;and the current API of plugin 0.1.0 allows me to do this! So I created the &lt;code&gt;pollen&lt;/code&gt; plugin. It takes all the commits from all branches and adds an &lt;code&gt;rss:item&lt;/code&gt; to a page.&lt;/p&gt;

&lt;p&gt;However, in order to achieve this, I opted to experiment with a different programming language (all the plugins are developed in Golang). I chose &lt;code&gt;assemblyscript&lt;/code&gt;, which is a subset of the &lt;code&gt;typescript&lt;/code&gt; language. So I added a new &lt;code&gt;plugin sdk&lt;/code&gt; in typescript.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To follow all gitroot commits follow &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/rss/all-commits.xml&quot;&gt;https://gitroot.dev/rss/all-commits.xml&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;lot-of-bugs&quot;&gt;Lot of bugs&lt;/h3&gt;

&lt;p&gt;During this first contribution, I discovered numerous bugs in the “grafter” plugin. From the &lt;a href=&quot;issues/close/v0.2.0/73e8-graft-diff-not-working.html&quot;&gt;diff rendering&lt;/a&gt; to &lt;a href=&quot;issues/close/v0.2.0/e188-graft-merge-not-wanted.html&quot;&gt;unwanted merging&lt;/a&gt; and &lt;a href=&quot;issues/close/9422-graft-need-3-review.html&quot;&gt;very complicated final merging&lt;/a&gt;, nothing worked! Even the basic GitRoot features were painful to use :D&lt;/p&gt;

&lt;h2 id=&quot;let-s-the-show-continue&quot;&gt;Let&amp;rsquo;s the show continue&lt;/h2&gt;

&lt;h3 id=&quot;no-linux-no-gitroot&quot;&gt;No linux == no gitroot&lt;/h3&gt;

&lt;p&gt;Surprisingly, someone wanted to compile gitroot on a Mac. Well, gitroot is actually written in Go, so there&amp;rsquo;s no problem doing that&amp;hellip; Except that I forgot to publish a fix I made to a dependency. Go couldn&amp;rsquo;t download the right dependency and therefore fails to compile completely&amp;hellip; But everything is OK now! I added an automatic compilation for Mac, you can find this on the &lt;a href=&quot;CHANGELOG.html&quot;&gt;version&lt;/a&gt; page.&lt;/p&gt;

&lt;h3 id=&quot;doc-doc-doc&quot;&gt;Doc, doc&amp;hellip; doc?&lt;/h3&gt;

&lt;p&gt;After that, someone tried to run gitroot on their local computer, as described in the &lt;a href=&quot;doc/tutorials/from_zero_to_hero.html&quot;&gt;document&lt;/a&gt;. However, when version &lt;code&gt;0.1.0&lt;/code&gt; was released, I discovered a bug. Gitroot cannot know where the &lt;code&gt;root&lt;/code&gt; repository is located (because you can change its name). To fix this, I added a configuration file before initialization, which had to be passed on launch&amp;hellip; It&amp;rsquo;s my fault, I forgot to update this tutorial.&lt;/p&gt;

&lt;h3 id=&quot;a-lot-more&quot;&gt;A lot more&lt;/h3&gt;

&lt;p&gt;As you can see, there were many mistakes in the first version. However, they have all been fixed, and, of course, I have added plenty more to keep me busy for the next version!&lt;/p&gt;

&lt;h3 id=&quot;more-technicals-bugs&quot;&gt;More technicals bugs&lt;/h3&gt;

&lt;p&gt;But I also fixed several important issues that were the result of some shortcuts that every project has to take for publication. One was about a &lt;a href=&quot;issues/close/v0.2.0/95b0-plugins-fs-bug.html&quot;&gt;FS bug&lt;/a&gt; when directories were empty, another concerned the &lt;a href=&quot;issues/close/v0.2.0/f1ab-garbage-collecte-plugins.html&quot;&gt;garbage collect plugin&lt;/a&gt; when the plugin had finished running, and many others.&lt;/p&gt;

&lt;h2 id=&quot;simplify-plugin-install&quot;&gt;Simplify plugin install&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;0.1.0&lt;/code&gt; version, to install a plugin you should download it, tell to gitroot where it is and then after push/pull activate/configure it. Now it is way more simple, just give an url, like &lt;code&gt;https://gitroot.dev/releases/0.2.0/pollen-0.0.1.wasm&lt;/code&gt; and optionnally (but encouraged) a checksum &lt;code&gt;sha256: 49cd8e1846b1612a0d5686918b502fe29e070e7bf9613c1ba9a1dfe5417d0f2a&lt;/code&gt;. Gitroot will download, check and run it. Of course you need to activate it. All url and checksum are in the &lt;a href=&quot;CHANGELOG.html&quot;&gt;versions page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have added a mecanisme to check that the plugin you try to run is compatble with your version of GitRoot. So launching &lt;code&gt;ladybug-0.0.1.wasm&lt;/code&gt; in &lt;code&gt;gitroot-0.2.0&lt;/code&gt; will log an error and not run the wasm. As GitRoot is in alpha version all new gitroot version will need plugins update.&lt;/p&gt;

&lt;h2 id=&quot;what-next&quot;&gt;What next?&lt;/h2&gt;

&lt;p&gt;With the exception of any new bugs detected for the next iteration, I would like to focus on the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;creating a plugins marketplace&lt;/li&gt;
&lt;li&gt;adding a new language SDK for plugins (Maybe Rust? Or Zig?)&lt;/li&gt;
&lt;li&gt;rebasing the Git lib fork and applying patches&lt;/li&gt;
&lt;/ul&gt;
</description><guid>https://gitroot.dev//blog/version-0.2.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.1.0.md</link><title>0.1.0</title><pubDate>2025/09/18</pubDate><description>&lt;h1 id=&quot;gitroot-0-1-0-is-deployed&quot;&gt;GitRoot 0.1.0 is deployed 🎉��&lt;/h1&gt;

&lt;h2 id=&quot;gitroot-in-numbers&quot;&gt;GitRoot in numbers:&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;58 commits&lt;/li&gt;
&lt;li&gt;+500 files &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/&quot;&gt;https://gitroot.dev/worktree/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;1 issue in gitlab (closed) &lt;a target=&quot;_blank&quot; href=&quot;https://gitlab.com/gitroot/gitroot/-/issues/1&quot;&gt;https://gitlab.com/gitroot/gitroot/-/issues/1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;29 issues close &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/boards/closed.html&quot;&gt;https://gitroot.dev/boards/closed.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;43 issues open &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/boards/issues.html&quot;&gt;https://gitroot.dev/boards/issues.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;22 pages de doc &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/doc/&quot;&gt;https://gitroot.dev/doc/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;4 licences &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/README.html#license&quot;&gt;https://gitroot.dev/README.html#license&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;4 plugins &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/CHANGELOG.html#plugins&quot;&gt;https://gitroot.dev/CHANGELOG.html#plugins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But if you retain only one, it is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 contributor &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/.gitroot/users.yml.html&quot;&gt;https://gitroot.dev/worktree/.gitroot/users.yml.html&lt;/a&gt; (others are forge and plugins 😭)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So there is only one thig to do&amp;hellip; read the doc and contribute!&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.1.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/security-vulnerability-1.md</link><title>blog/security-vulnerability-1.md</title><pubDate>2026/03/30</pubDate><description>&lt;h1 id=&quot;security-advisory-executor-command-injection&quot;&gt;Security Advisory: Executor Command Injection&lt;/h1&gt;

&lt;h2 id=&quot;1-executive-summary&quot;&gt;1. Executive Summary&lt;/h2&gt;

&lt;p&gt;A vulnerability has been identified within the GitRoot &lt;strong&gt;executors&lt;/strong&gt; system. Under specific conditions, it is possible for a malicious actor to execute unauthorized commands on the host machine. Given the complexity of the required conditions, this vulnerability is not easily exploitable in a standard production environment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Severity:&lt;/strong&gt; Moderate&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Affected Version:&lt;/strong&gt; 0.3.0&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fix Status:&lt;/strong&gt; Scheduled for version 0.4.0&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;2-technical-details&quot;&gt;2. Technical Details&lt;/h2&gt;

&lt;p&gt;The vulnerability stems from the way executors validate commands defined by plugins. Currently, a plugin can declare a benign command in its manifest (e.g., &lt;code&gt;cat&lt;/code&gt;), but execute an arbitrary command during runtime (e.g., &lt;code&gt;rm -rf data/&lt;/code&gt;). GitRoot currently lacks a strict enforcement mechanism to verify that the executed binary matches the declared command.&lt;/p&gt;

&lt;h2 id=&quot;3-impact-analysis&quot;&gt;3. Impact Analysis&lt;/h2&gt;

&lt;p&gt;The level of risk depends on your infrastructure configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;High Risk:&lt;/strong&gt; Users utilizing &lt;code&gt;bareMetal&lt;/code&gt; or &lt;code&gt;ssh&lt;/code&gt; executors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Low Risk:&lt;/strong&gt; Users utilizing &lt;code&gt;bubblewrap&lt;/code&gt; or &lt;code&gt;container&lt;/code&gt; isolation (as the sandbox significantly limits the potential impact on the host).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exploitability Constraints:&lt;/strong&gt;
This vulnerability is not a &amp;ldquo;remote code execution&amp;rdquo; flaw in the traditional sense. Exploitation requires:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Installing a malicious plugin.&lt;/li&gt;
&lt;li&gt;Having write access to the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt; of the root repository.&lt;/li&gt;
&lt;li&gt;Merging a malicious &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt; into the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Currently, creating plugins is undocumented, and the only known plugin using executors is &lt;code&gt;hop&lt;/code&gt;. Since any modification to &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt; requires a merge into the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt;, a standard code review process should easily detect and prevent such malicious entries.&lt;/p&gt;

&lt;h2 id=&quot;4-immediate-mitigations&quot;&gt;4. Immediate Mitigations&lt;/h2&gt;

&lt;p&gt;If you cannot wait for the 0.4.0 release, we recommend applying the following measures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Disable Executors:&lt;/strong&gt; Set your forge configuration for executors to &lt;code&gt;None&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secure Executors:&lt;/strong&gt; Use more secure executors, such as &lt;code&gt;bwrap&lt;/code&gt; or &lt;code&gt;container&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plugin Trust:&lt;/strong&gt; Only install and enable plugins from trusted sources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access Control:&lt;/strong&gt; Strictly monitor and review all changes to &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt;, specifically focusing on authorized system commands.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;5-patch-schedule&quot;&gt;5. Patch Schedule&lt;/h2&gt;

&lt;p&gt;This vulnerability will be fully addressed in &lt;strong&gt;version 0.4.0&lt;/strong&gt;.
We have decided not to release an intermediate 0.3.1 patch, as the attack vector relies on an &amp;ldquo;insider&amp;rdquo; or &amp;ldquo;compromised account&amp;rdquo; scenario that is effectively mitigated by standard code review practices.&lt;/p&gt;

&lt;h2 id=&quot;6-security-contact&quot;&gt;6. Security Contact&lt;/h2&gt;

&lt;p&gt;Transparency and security are core values at GitRoot. If you discover any other potential vulnerabilities or have questions regarding this report, please contact us directly at &lt;a href=&quot;blog/contact@gitroot.dev&quot;&gt;contact@gitroot.dev&lt;/a&gt;.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/security-vulnerability-1.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/modifying-API-SDK-plugin.md</link><title>Modifying the API in the SDK Plugin</title><pubDate>2026/04/06</pubDate><description>&lt;h1 id=&quot;modifying-the-api-in-the-sdk-plugin&quot;&gt;Modifying the API in the SDK Plugin&lt;/h1&gt;

&lt;p&gt;The last feature for version 0.4 is about adding &lt;code&gt;mv&lt;/code&gt; (move) and &lt;code&gt;cp&lt;/code&gt; (copy) functions to the SDK plugin. I&amp;rsquo;m taking advantage of this development to explain how the plumbing works under the hood.&lt;/p&gt;

&lt;p&gt;Right now, the SDK plugin is undocumented because there are a lot of breaking changes coming. Every time I develop a new plugin, I discover better ways to do things, and the SDK needs to adapt.&lt;/p&gt;

&lt;p&gt;For example, when I added &lt;code&gt;apex_code&lt;/code&gt; (a plugin that takes code as a string and returns syntax-highlighted HTML), I wondered: &amp;ldquo;Is this a standalone plugin?&amp;rdquo;. Not really, because there&amp;rsquo;s no file to manipulate, neither in input nor output. Instead, it manages blocks inside an existing file. The SDK wasn&amp;rsquo;t originally built with that use case in mind.&lt;/p&gt;

&lt;p&gt;Anyway, I think explaining how the SDK works through a concrete example will be helpful, if not for you, at least for me.&lt;/p&gt;

&lt;h2 id=&quot;what-is-the-sdk-s-job&quot;&gt;What is the SDK&amp;rsquo;s job?&lt;/h2&gt;

&lt;p&gt;The SDK exists to pass data from GitRoot (the host) to the plugin (the guest). As you know, GitRoot uses WebAssembly (WASM) to manage plugins. Unfortunately, WASM is still a bit low-level to implement: between the host and the guest, only integers can travel.&lt;/p&gt;

&lt;p&gt;Wait, only integers?&lt;/p&gt;

&lt;p&gt;Yes. So, how do we send a string? The host can store any kind of data in the guest&amp;rsquo;s memory. GitRoot stores a string, retrieves the pointer (the memory address) and the size, and sends those two integers to the guest. The guest then retrieves the string from its own memory. It&amp;rsquo;s a complex dance just for a simple string. The SDK is here to hide all that mess.&lt;/p&gt;

&lt;p&gt;When you build a plugin, you just implement an interface. The SDK handles the rest: you receive objects and strings, and you send back data without ever worrying about the underlying WASM pointers.&lt;/p&gt;

&lt;h2 id=&quot;what-does-a-concrete-implementation-look-like&quot;&gt;What does a concrete implementation look like?&lt;/h2&gt;

&lt;p&gt;When I add a new function, I always start with the host side. All functions are located in &lt;a href=&quot;app/server/plugin/runtime.go&quot;&gt;runtime.go&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We need to register the function in &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/wazero/wazero&quot;&gt;wazero&lt;/a&gt;, our WASM runtime. I add two versions because I haven&amp;rsquo;t found a clean way to bridge the difference between AssemblyScript and Go/Rust yet. Hopefully, I&amp;rsquo;ll figure that out one day.&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;r.wazRun.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#50fa7b&quot;&gt;NewFunctionBuilder&lt;/span&gt;().&lt;span style=&quot;color:#50fa7b&quot;&gt;WithFunc&lt;/span&gt;(r.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(&lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;)).&lt;span style=&quot;color:#50fa7b&quot;&gt;Export&lt;/span&gt;(&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;moveFile&amp;#34;&lt;/span&gt;).
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#50fa7b&quot;&gt;NewFunctionBuilder&lt;/span&gt;().&lt;span style=&quot;color:#50fa7b&quot;&gt;WithFunc&lt;/span&gt;(r.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(&lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;)).&lt;span style=&quot;color:#50fa7b&quot;&gt;Export&lt;/span&gt;(&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;moveFileAS&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I implement the builder:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(forAS &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;interface&lt;/span&gt;{} {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; !forAS {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, ptr, size, ptr2, size2 &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#6272a4&quot;&gt;// from ptr/size to string or object
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;			r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromPath, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	} &lt;span style=&quot;color:#ff79c6&quot;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, ptr, ptr2 &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#6272a4&quot;&gt;// from ptr to string or object
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;			r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromPath, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Yeah, I know. As a Gopher, seeing &lt;code&gt;interface{}&lt;/code&gt; as a return type and all these uint32 feels wrong. But hey, we&amp;rsquo;re not here for a code review, we&amp;rsquo;re here to add an API. If you have a cleaner way, feel free to submit a graft!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We want to allow any plugin to move a file. Let&amp;rsquo;s define the internal &lt;code&gt;moveFile&lt;/code&gt; function:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromPath, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#6272a4&quot;&gt;//need to do something...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Actually, there&amp;rsquo;s a catch, GitRoot uses three internal filesystems: one for the git worktree, one for web content, and one for the cache. Even if they are abstracted under one root, the code often expects a specific FS. I&amp;rsquo;ll probably clean this up later, but for now:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#6272a4&quot;&gt;//need to do something...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now your plugin can call &lt;code&gt;moveFile(&amp;quot;cache&amp;quot;, &amp;quot;artifacts/myBin&amp;quot;, &amp;quot;web&amp;quot;, &amp;quot;releases/myBin-v1.0&amp;quot;)&lt;/code&gt; to publish an artifact, or &lt;code&gt;moveFile(&amp;quot;web&amp;quot;, &amp;quot;index.html&amp;quot;, &amp;quot;web&amp;quot;, &amp;quot;old/index.html&amp;quot;)&lt;/code&gt; to back up a file.&lt;/p&gt;

&lt;p&gt;I won&amp;rsquo;t bore you with the implementation logic here. If you&amp;rsquo;re curious, check the &lt;a href=&quot;app/server/plugin/runtime.go&quot;&gt;code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is how the final builder looks, converting pointers to strings:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(forAS &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;interface&lt;/span&gt;{} {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; !forAS {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, fromFsPtr, fromFsSize, fromPathPtr, fromGlobPathSize, toFsPtr, toFsSize, toPathPtr, toPathSize &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;uint64&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile fromFs&amp;#34;&lt;/span&gt;, fromFsPtr, fromFsSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile fromGlobPath&amp;#34;&lt;/span&gt;, fromPathPtr, fromGlobPathSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile toFs&amp;#34;&lt;/span&gt;, toFsPtr, toFsSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile toPath&amp;#34;&lt;/span&gt;, toPathPtr, toPathSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	} &lt;span style=&quot;color:#ff79c6&quot;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, fromFsPtr, fromPathPtr, toFsPtr, toPathPtr &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;uint64&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS fromFs&amp;#34;&lt;/span&gt;, fromFsPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS fromGlobPath&amp;#34;&lt;/span&gt;, fromPathPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS toFs&amp;#34;&lt;/span&gt;, toFsPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS toPath&amp;#34;&lt;/span&gt;, toPathPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L38&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L38&quot;&gt;38&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L39&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L39&quot;&gt;39&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L40&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L40&quot;&gt;40&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L41&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L41&quot;&gt;41&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L42&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L42&quot;&gt;42&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L43&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L43&quot;&gt;43&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When I need to pass complex objects, I use &lt;code&gt;json.Marshal&lt;/code&gt; in GitRoot and &lt;code&gt;json.Unmarshal&lt;/code&gt; in the SDK. It works, but don&amp;rsquo;t get too attached to it, I might change the serialization method in the future.&lt;/p&gt;

&lt;h2 id=&quot;inside-the-sdk&quot;&gt;Inside the SDK&lt;/h2&gt;

&lt;p&gt;Now that the host is ready, we need to implement the other side: the SDK itself. I usually start with Go, then move to Rust and AssemblyScript. Sometimes I forget the others&amp;hellip; shame on me! I&amp;rsquo;m planning to automate this with some boilerplate generation later.&lt;/p&gt;

&lt;p&gt;In the &lt;a href=&quot;app/libs/golang/plugin/model/server.go&quot;&gt;server&lt;/a&gt; interface:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;type&lt;/span&gt; Server &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;interface&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#6272a4&quot;&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;    &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#6272a4&quot;&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make it more &amp;ldquo;user-friendly&amp;rdquo;, I use a struct to represent the filesystem in &lt;a href=&quot;app/libs/golang/plugin/model/fs.go&quot;&gt;model/fs.go&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (g &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;GrFs) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(fromPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; g.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(g.base, fromPath, g.base, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (g &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;GrFs) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileToFs&lt;/span&gt;(fromPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, toFs &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;GrFs, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; g.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(g.base, fromPath, toFs.base, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The actual WASM call happens in &lt;a href=&quot;app/libs/golang/plugin/server.go&quot;&gt;plugin/server.go&lt;/a&gt;, where we transform strings back into pointers:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;// MoveFile implements [model.ServerNeeded].
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (s &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;pServer) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(fromFs model.FsBase, fromPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, toFs model.FsBase, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr, size &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;string&lt;/span&gt;(fromFs))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr2, size2 &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(fromPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr3, size3 &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;string&lt;/span&gt;(toFs))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr4, size4 &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	resPtrSize &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;_moveFile&lt;/span&gt;(ptr, size, ptr2, size2, ptr3, size3, ptr4, size4)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(fromFs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(fromPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(toFs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;ptrSizeToError&lt;/span&gt;(resPtrSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally, the link to the host via &lt;code&gt;go:wasmimport&lt;/code&gt; in &lt;a href=&quot;app/libs/golang/plugin/imports.go&quot;&gt;plugin/imports.go&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;//go:wasmimport gitroot moveFile
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;_moveFile&lt;/span&gt;(fromFsPtr, fromFsSize, fromPathPtr, fromPathSize, toFsPtr, toFsSize, toPathPtr, toPathSize &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) (ptrSize &lt;span style=&quot;color:#8be9fd&quot;&gt;uint64&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;how-to-use-it&quot;&gt;How to use it?&lt;/h2&gt;

&lt;p&gt;Now, any plugin can use this. Here&amp;rsquo;s a simple Go plugin that triggers a build whenever a &lt;code&gt;.go&lt;/code&gt; file is modified and moves the binary to the web folder.&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;package&lt;/span&gt; main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;path/filepath&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;strings&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	gitroot &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;gitroot.dev/libs/golang/plugin&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;gitroot.dev/libs/golang/plugin/model&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;var&lt;/span&gt; defaultRun = []model.PluginRun{{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	Path:   &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*.go&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	When:   model.PluginRunWhenAll,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	Branch: []&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;{&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	Write: model.PluginWrite{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		Web: []model.PluginWriteRight{{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			Path: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;releases/main&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			Can:  model.PluginWriteRightCanAll,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;type&lt;/span&gt; Plugin &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	server model.Server
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    needToBuild &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;Init&lt;/span&gt;(repoName &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, confHasChanged &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;, serializedConf &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;StartCommit&lt;/span&gt;(commit model.Commit) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                  { &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;AddFile&lt;/span&gt;(file model.File) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L38&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L38&quot;&gt;38&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L39&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L39&quot;&gt;39&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L40&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L40&quot;&gt;40&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L41&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L41&quot;&gt;41&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;ModFile&lt;/span&gt;(file model.File) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L42&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L42&quot;&gt;42&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L43&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L43&quot;&gt;43&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L44&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L44&quot;&gt;44&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L45&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L45&quot;&gt;45&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L46&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L46&quot;&gt;46&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;DelFile&lt;/span&gt;(file model.File) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L47&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L47&quot;&gt;47&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L48&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L48&quot;&gt;48&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L49&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L49&quot;&gt;49&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L50&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L50&quot;&gt;50&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L51&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L51&quot;&gt;51&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;EndCommit&lt;/span&gt;(commit model.Commit) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                    { &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L52&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L52&quot;&gt;52&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L53&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L53&quot;&gt;53&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;Finish&lt;/span&gt;() &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L54&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L54&quot;&gt;54&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; p.needToBuild {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L55&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L55&quot;&gt;55&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        res, _ &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; p.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;Exec&lt;/span&gt;(model.Exec{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L56&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L56&quot;&gt;56&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            Cmds: []model.Cmd{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L57&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L57&quot;&gt;57&lt;/a&gt;&lt;/span&gt;&lt;span&gt;                {Cmd: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;go&amp;#34;&lt;/span&gt;, Args: []&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;{&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;build&amp;#34;&lt;/span&gt;}},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L58&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L58&quot;&gt;58&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L59&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L59&quot;&gt;59&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            Artifacts: []&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;{&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L60&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L60&quot;&gt;60&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L61&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L61&quot;&gt;61&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; p.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;Cache&lt;/span&gt;().&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileToFs&lt;/span&gt;(res.Artifacts[&lt;span style=&quot;color:#bd93f9&quot;&gt;0&lt;/span&gt;], p.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;Webcontent&lt;/span&gt;(), filepath.&lt;span style=&quot;color:#50fa7b&quot;&gt;Join&lt;/span&gt;(&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;releases&amp;#34;&lt;/span&gt;, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L62&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L62&quot;&gt;62&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L63&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L63&quot;&gt;63&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L64&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L64&quot;&gt;64&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L65&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L65&quot;&gt;65&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L66&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L66&quot;&gt;66&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;Build&lt;/span&gt;(server model.Server) model.Plugin {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L67&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L67&quot;&gt;67&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;&amp;amp;&lt;/span&gt;Plugin{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L68&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L68&quot;&gt;68&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		server: server,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L69&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L69&quot;&gt;69&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L70&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L70&quot;&gt;70&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L71&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L71&quot;&gt;71&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L72&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L72&quot;&gt;72&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;//go:wasmexport install
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L73&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L73&quot;&gt;73&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L74&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L74&quot;&gt;74&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	gitroot.&lt;span style=&quot;color:#50fa7b&quot;&gt;Register&lt;/span&gt;(defaultRun, Build)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L75&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L75&quot;&gt;75&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;is-that-all&quot;&gt;Is that all?&lt;/h2&gt;

&lt;p&gt;Nope. I still need to update the &lt;a href=&quot;app/libs/rust/plugin/src/fs.rs&quot;&gt;Rust&lt;/a&gt; and &lt;a href=&quot;app/libs/ts/plugin/fs.ts&quot;&gt;AssemblyScript&lt;/a&gt; SDKs and, of course, add &lt;a href=&quot;app/libs/golang/plugin/test/fakeserver.go&quot;&gt;tests&lt;/a&gt;. But this should give you a good idea of how the plumbing works.&lt;/p&gt;

&lt;p&gt;If anything is unclear, &lt;a href=&quot;contact.html&quot;&gt;let me know&lt;/a&gt;!&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/modifying-API-SDK-plugin.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/how-to-help_fr_11-2025.md</link><title>Comment contribuer au développement de GitRoot</title><pubDate>2025/11/18</pubDate><description>&lt;h1 id=&quot;comment-contribuer-au-développement-de-gitroot-11-2025&quot;&gt;Comment contribuer au développement de GitRoot 11-2025&lt;/h1&gt;

&lt;p&gt;Je viens tout juste de lire &lt;a target=&quot;_blank&quot; href=&quot;https://antonin.delpeuch.eu/posts/too-much-time-on-your-hands/&quot;&gt;cette article&lt;/a&gt; et je n&amp;rsquo;arrête pas d&amp;rsquo;y penser. Je le met en perspective des contributeurs de GitRoot. Je suis le seul contributeur (à part mon ami Damien qui a corrigé de nombreuses fautes, merci à lui) sur ce projet. Et comme le montre ce &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge/statuses/01K98BCFEB9V430JD9TFQ32YZN&quot;&gt;toot&lt;/a&gt; j&amp;rsquo;ai un travail pour payer les factures, 1 femme et 2 enfants qui m&amp;rsquo;occupent pratiquement tout le reste du temps. À ça je rajoute GitRoot, pour le moment je m&amp;rsquo;éclate et je tiens bon mais pour combien de temps je vais y arriver ?&lt;/p&gt;

&lt;p&gt;Alors pourquoi pas de contributeurs externes sur GitRoot ?&lt;/p&gt;

&lt;p&gt;Je me dit souvent que le projet est jeune, trop jeune, pour que ça soit accessible à un developpeur externe. Parfois je me dit qu&amp;rsquo;un meilleurs système de ticket serait nécessaire pour avoir un label &amp;ldquo;welcome new contribution&amp;rdquo;. Parfois je me dit qu&amp;rsquo;une fois que j&amp;rsquo;aurais déployé les plugins sdk et fait un market place de plugins, enfin vous pourrez venir m&amp;rsquo;aider à faire que GitRoot soit un vrai projet open-source.&lt;/p&gt;

&lt;p&gt;Mais, car il y a un mais, cette liste ne s&amp;rsquo;arrêtera jamais. Soyons franc il y aura toujours un truc de plus à faire avant que vous puissiez m&amp;rsquo;aider à faire que GitRoot soit au top. J&amp;rsquo;ai donc décidé de faire la chose la plus simple à l&amp;rsquo;heure actuelle pour vous expliquer comment m&amp;rsquo;aider: un article de blog.&lt;/p&gt;

&lt;p&gt;Ici, je vais vous expliquer tout ce qu&amp;rsquo;il est possible de faire en l&amp;rsquo;état actuel du projet. Pas ce qu&amp;rsquo;il sera possible de faire un jour &lt;em&gt;peu être&lt;/em&gt;, mais ce que vous pouvez faire là maintenant. Je réitèrerais ce genre d&amp;rsquo;article tant que nécessaire.&lt;/p&gt;

&lt;h2 id=&quot;pré-requis&quot;&gt;Pré-requis&lt;/h2&gt;

&lt;p&gt;Il n&amp;rsquo;y a pas grand chose à faire pour être prêt à aider sur GitRoot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;vous devez avoir &lt;a target=&quot;_blank&quot; href=&quot;https://git-scm.com/&quot;&gt;git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;git clone ssh://gitroot.dev&lt;/li&gt;
&lt;li&gt;cd gitroot.dev&lt;/li&gt;
&lt;li&gt;./gitroot/init.sh&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si vous souhaitez coder il vous faudra en plus:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/bash/manual/bash.html&quot;&gt;bash&lt;/a&gt; et &lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/make/&quot;&gt;make&lt;/a&gt; (si vous êtes dev il y a de grandes chances que vous les aillez déjà)&lt;/li&gt;
&lt;li&gt;installer &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt;, c&amp;rsquo;est le plus simple pour avoir tous les compilateurs dans les bonnes versions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Et voila vous êtes bon pour commencer à aider. Mais que faire ? Et bien comme tout bon developpeur je vais vous répondre &amp;ldquo;ça dépend&amp;rdquo;. Ça dépend de ce que vous voulez faire ! Je vais lister dessous un ensemble de taches possibles, vous choisissez.&lt;/p&gt;

&lt;h2 id=&quot;avant-de-commencer&quot;&gt;Avant de commencer&lt;/h2&gt;

&lt;p&gt;Avant de vous dire ce que vous pouvez faire, je me dit qu&amp;rsquo;il serait bien de vous expliquer ce que moi je fais. Donc pour cette version 0.3.0 ma priorité absolue est de faire en sorte que GitRoot build GitRoot lui même. Aujourd&amp;rsquo;hui à chaque release je fais les binaires sur ma macine et je les scp. Je veux qu&amp;rsquo;avec cette version 0.3.0 cela ne soit plus le cas.&lt;/p&gt;

&lt;p&gt;Donc, si j&amp;rsquo;arrive à me retenir, je ne devrais pas faire les tickets qui suivent. Vous avez tout le temps dont vous souhaitez pour les faire. Le plus simple est, je pense, de nous retrouver sur &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;matrix channel gitroot&lt;/a&gt; pour en discutter.&lt;/p&gt;

&lt;p&gt;On peut également en parler par mail &lt;a href=&quot;blog/contact@gitroot.dev&quot;&gt;contact@gitroot.dev&lt;/a&gt; ou sur le fediverse &lt;cite class=&quot;informative&quot;&gt;&lt;a href=&quot;#forge@gitrrot.dev&quot;&gt;&lt;sup&gt;[forge@gitrrot.dev]&lt;/sup&gt;&lt;/a&gt;&lt;/cite&gt;(&lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;https://gts.gitroot.dev/@forge&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;qu-est-ce-qu-on-peut-faire&quot;&gt;Qu&amp;rsquo;est-ce qu&amp;rsquo;on peut faire ?&lt;/h2&gt;

&lt;h3 id=&quot;corriger-les-fautes&quot;&gt;Corriger les fautes&lt;/h3&gt;

&lt;p&gt;L&amp;rsquo;anglais n&amp;rsquo;est pas ma langue d&amp;rsquo;origine et je pense qu&amp;rsquo;il y a beaucoup de fautes, donc une tache pourrais être de lire tous les md dans &lt;code&gt;/doc/&lt;/code&gt;, &lt;code&gt;/blog/&lt;/code&gt; et pourquoi pas &lt;code&gt;/issues/&lt;/code&gt;. Vous corrigez, faites une branche et poussez. Je m&amp;rsquo;occupe du reste !&lt;/p&gt;

&lt;h3 id=&quot;moi-je-veux-faire-du-web&quot;&gt;Moi je veux faire du web&lt;/h3&gt;

&lt;p&gt;Ce qui serait top, ce serait de créer (ou de trouver) un thème. Aujourd&amp;rsquo;hui GitRoot utilise &lt;a target=&quot;_blank&quot; href=&quot;https://simplecss.org/&quot;&gt;simplecss&lt;/a&gt;, et il n&amp;rsquo;a besoin que de &lt;code&gt;css classless&lt;/code&gt;, il en existe beaucoup mais vous pouvez aussi créer celui de GitRoot.&lt;/p&gt;

&lt;p&gt;Le plus simple pour essayer est de lancer la commande &lt;code&gt;make testsuite&lt;/code&gt; quand elle a fini rendez vous dans &lt;code&gt;cd /tmp/repo1&lt;/code&gt; éditez le fichier &lt;code&gt;style.css&lt;/code&gt; puis &lt;code&gt;git add .&lt;/code&gt; &lt;code&gt;git commit -m &amp;quot;try style&amp;quot;&lt;/code&gt; et &lt;code&gt;git push&lt;/code&gt;, ouvrez &lt;a target=&quot;_blank&quot; href=&quot;http://127.0.0.1:4546/repo1&quot;&gt;http://127.0.0.1:4546/repo1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Quand vous êtes content faites une branche avec votre css dans &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/resources/styles/&quot;&gt;apex&lt;/a&gt;, puis &lt;code&gt;git add .&lt;/code&gt; et &lt;code&gt;git push&lt;/code&gt; je m&amp;rsquo;occupe du reste.&lt;/p&gt;

&lt;h3 id=&quot;moi-je-veux-coder&quot;&gt;Moi je veux coder&lt;/h3&gt;

&lt;p&gt;Bien, pas de soucis, voici une liste de petites tâches (enfin elles me paraissent petites mais si c&amp;rsquo;est pas le cas pour vous dites le moi):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;en golang:

&lt;ul&gt;
&lt;li&gt;dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/grafter/&quot;&gt;grafter&lt;/a&gt; pouvoir afficher &lt;a href=&quot;issues/3d33-grafter-dco.html&quot;&gt;autre chose&lt;/a&gt; dans le 1er message d&amp;rsquo;un graft. La bannière est en dure dans le plugin, il faudrait l&amp;rsquo;exporter dans la conf du plugin et l&amp;rsquo;utiliser.&lt;/li&gt;
&lt;li&gt;dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/silo/&quot;&gt;silo&lt;/a&gt; permettre de &lt;a href=&quot;issues/76ec-silo-more-selects.html&quot;&gt;grouper par fichier ou date&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;plus difficile mais j&amp;rsquo;aimerais bien avoir plus de &lt;a href=&quot;issues/e6d8-apex-alert-support.html&quot;&gt;renderer&lt;/a&gt; dans &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/&quot;&gt;apex&lt;/a&gt;, par exemple les infos, warning et autre&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;en typescript:

&lt;ul&gt;
&lt;li&gt;dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; il y a un bug sur le rss &lt;a href=&quot;issues/3986-pollen-rss-grow-infinite.html&quot;&gt;il est jamais nettoyé&lt;/a&gt;. Rajouté une option (ou pas, 100 en dur me semble être déjà pas mal) pour limiter le nombre d&amp;rsquo;&lt;code&gt;&amp;lt;item&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;toujours dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; pourquoi pas rajouter un &lt;a target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/JSON_Feed&quot;&gt;json feed&lt;/a&gt; en plus du rss. On peut serializer du json en assemblescript.&lt;/li&gt;
&lt;li&gt;plus difficile mais il serait bien de pouvoir dire au plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; de générer un rss avec le contenu des fichiers. Ça permettrait à GitRoot d&amp;rsquo;avoir un rss affichant les articles du blog par exemple. Aujourd&amp;rsquo;hui il ne met dans le rss que les commits.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;en rust:

&lt;ul&gt;
&lt;li&gt;faire ce que Ygor à dit dans &lt;a target=&quot;_blank&quot; href=&quot;https://floss.social/@ygor/115474535138702961&quot;&gt;https://floss.social/@ygor/115474535138702961&lt;/a&gt; je ne suis pas un developpeur rust et transformer des &lt;code&gt;String&lt;/code&gt; en &lt;code&gt;&amp;amp;str&lt;/code&gt; me parle pas beaucoup mais si vous l&amp;rsquo;êtes je suis sûr que c&amp;rsquo;est une petite tâche. Le code se trouve dans le &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; et le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/hop/&quot;&gt;hop&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;ajouter un &lt;a href=&quot;issues/9f00-rust-sdk-fake-server-test.html&quot;&gt;fakeServer&lt;/a&gt; au &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; pour faciliter les tests d&amp;rsquo;un plugin, voici celui de go &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&quot;&gt;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;moi-je-suis-sysadmin&quot;&gt;Moi je suis sysadmin&lt;/h3&gt;

&lt;p&gt;Parfait il y a de quoi faire aussi:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;aujourd&amp;rsquo;hui il y a trop d&amp;rsquo;étapes pour releaser GitRoot, &lt;a href=&quot;issues/dba0-simplify-release.html&quot;&gt;automatiser&lt;/a&gt; au maximum m&amp;rsquo;aiderais beaucoup.&lt;/li&gt;
&lt;li&gt;si vous avez un petit de place sur un de vos serveurs, &lt;a href=&quot;doc/tutorials/deploy_instance.html&quot;&gt;tester de déployer une instance&lt;/a&gt; juste pour le fun. Peu être vous pourrez améliorer la doc ou trouver quelque chose qui ne marche plus. Ou ajouter un paragraphe pour utiliser gitroot avec nginx ou autre.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;moi-je-veux-tester&quot;&gt;Moi je veux tester&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;tous les tests sont en bash dans &lt;a href=&quot;app/testsuite/runtest.sh&quot;&gt;testsuite/runtest.sh&lt;/a&gt; se trouve le plus gros test, à côté il y en d&amp;rsquo;autres

&lt;ul&gt;
&lt;li&gt;exécutez les pour voir s&amp;rsquo;ils marchent chez vous me parait une bonne première tâche&lt;/li&gt;
&lt;li&gt;en créer de nouveaux&lt;/li&gt;
&lt;li&gt;améliorer les existants en sortant par exemple des fonctions helper comme &lt;code&gt;quiet_git()&lt;/code&gt;, &lt;code&gt;report()&lt;/code&gt; ou &lt;code&gt;mySleep()&lt;/code&gt; dans d&amp;rsquo;autres fichiers (et les importer) serait d&amp;rsquo;une grande aide pour le projet. Vous pouvez aussi créer des nouveaux pour &lt;code&gt;killGitroot()&lt;/code&gt; qui est fait en début de chaque fichier. Ou &lt;code&gt;createProject()&lt;/code&gt; qui consiste à chaque fois à aller dans le root repository et ajouter son nom dans le fichier &lt;code&gt;.gitroot/repositories.yml&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;moi-je-veux-juste-réfléchir-discutter&quot;&gt;Moi je veux juste réfléchir/discutter&lt;/h3&gt;

&lt;p&gt;Tu es le bienvenue sur le &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;channel gitroot&lt;/a&gt; dans matrix. Ou par mail &lt;code&gt;contact [AT] gitroot.dev&lt;/code&gt; ou sur le fediverse &lt;code&gt;@forge@gitroot.dev&lt;/code&gt; &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;sur ce serveur&lt;/a&gt;.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/how-to-help_fr_11-2025.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/how-to-help_en_11-2025.md</link><title>How to help building GitRoot</title><pubDate>2025/11/18</pubDate><description>&lt;h1 id=&quot;how-to-help-building-gitroot-11-2025&quot;&gt;How to help building GitRoot 11-2025&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;This article is the translation of &lt;a href=&quot;blog/how-to-help_fr_11-2025.html&quot;&gt;french version&lt;/a&gt; thanks to &lt;a target=&quot;_blank&quot; href=&quot;https://www.deepl.com&quot;&gt;DeepL&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I just read &lt;a target=&quot;_blank&quot; href=&quot;https://antonin.delpeuch.eu/posts/too-much-time-on-your-hands/&quot;&gt;this article&lt;/a&gt; and I can&amp;rsquo;t stop thinking about it. I&amp;rsquo;m putting it into perspective for GitRoot contributors. I&amp;rsquo;m the only contributor (apart from my friend Damien, who corrected numerous typos, thank you to him) on this project. And as this &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge/statuses/01K98BCFEB9V430JD9TFQ32YZN&quot;&gt;toot&lt;/a&gt; shows, I have a job to pay the bills, a wife and two children who take up practically all the rest of my time. On top of that, I have GitRoot. For now, I&amp;rsquo;m having a blast and hanging in there, but how long can I keep it up?&lt;/p&gt;

&lt;p&gt;So why not have external contributors on GitRoot?&lt;/p&gt;

&lt;p&gt;I often tell myself that the project is young, too young, to be accessible to an external developer. Sometimes I think that a better issues system would be necessary to have a “welcome new contribution” label. Sometimes I think that once I&amp;rsquo;ve deployed the SDK plugins and created a plugin marketplace, then you can come and help me make GitRoot a true open-source project.&lt;/p&gt;

&lt;p&gt;But, because there is a but, this list will never end. Let&amp;rsquo;s be honest, there will always be something else to do before you can help me make GitRoot the best it can be. So I decided to do the simplest thing possible right now to explain how you can help me: a blog post.&lt;/p&gt;

&lt;p&gt;Here, I&amp;rsquo;ll explain everything that can be done in the current state of the project. Not what might be possible someday, but what you can do right now. I&amp;rsquo;ll repeat this kind of article as often as necessary.&lt;/p&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;There isn&amp;rsquo;t much you need to do to get ready to help out on GitRoot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you need to have &lt;a target=&quot;_blank&quot; href=&quot;https://git-scm.com/&quot;&gt;git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;git clone ssh://gitroot.dev&lt;/li&gt;
&lt;li&gt;cd gitroot.dev&lt;/li&gt;
&lt;li&gt;./gitroot/init.sh&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to code, you will also need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/bash/manual/bash.html&quot;&gt;bash&lt;/a&gt; and &lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/make/&quot;&gt;make&lt;/a&gt; (if you are a developer, chances are you already have them)&lt;/li&gt;
&lt;li&gt;Install &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt;, which is the easiest way to get all the right versions of the compilers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And now you&amp;rsquo;re ready to start helping. But what should you do? Well, like any good developer, I&amp;rsquo;ll answer, &amp;ldquo;It depends.&amp;rdquo;. It depends on what you want to do! I&amp;rsquo;ll list a set of possible tasks below, and you can choose.&lt;/p&gt;

&lt;h2 id=&quot;before-we-begin&quot;&gt;Before we begin&lt;/h2&gt;

&lt;p&gt;Before telling you what you can do, I thought it would be a good idea to explain what I am doing. So, for this version 0.3.0, my top priority is to make sure that GitRoot builds GitRoot itself. Currently, for each release, I build the binaries on my machine and then scp them. I want this to no longer be the case with version 0.3.0.&lt;/p&gt;

&lt;p&gt;So, if I can restrain myself, I shouldn&amp;rsquo;t have to do the following tickets. You have all the time you need to do them. The easiest thing, I think, is to meet up on &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;matrix channel gitroot&lt;/a&gt; to discuss them.&lt;/p&gt;

&lt;p&gt;We can also talk about it by email &lt;a href=&quot;blog/contact@gitroot.dev&quot;&gt;contact@gitroot.dev&lt;/a&gt; or on the fediverse &lt;cite class=&quot;informative&quot;&gt;&lt;a href=&quot;#forge@gitrrot.dev&quot;&gt;&lt;sup&gt;[forge@gitrrot.dev]&lt;/sup&gt;&lt;/a&gt;&lt;/cite&gt;(&lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;https://gts.gitroot.dev/@forge&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;what-can-we-do&quot;&gt;What can we do?&lt;/h2&gt;

&lt;h3 id=&quot;correct-mistakes&quot;&gt;Correct mistakes&lt;/h3&gt;

&lt;p&gt;English is not my native language, and I think there are a lot of typos, so one task could be to read all the MD files in &lt;code&gt;/doc/&lt;/code&gt;, &lt;code&gt;/blog/&lt;/code&gt;, and maybe even &lt;code&gt;/issues/&lt;/code&gt;. Correct them, create a branch, and push. I&amp;rsquo;ll take care of the rest!&lt;/p&gt;

&lt;h3 id=&quot;i-want-to-work-on-the-web&quot;&gt;I want to work on the web&lt;/h3&gt;

&lt;p&gt;It would be great to create (or find) a theme. GitRoot currently uses &lt;a target=&quot;_blank&quot; href=&quot;https://simplecss.org/&quot;&gt;simplecss&lt;/a&gt;, and it only needs &lt;code&gt;classless CSS&lt;/code&gt;. There are many available, but you can also create one for GitRoot.&lt;/p&gt;

&lt;p&gt;The easiest way to try it out is to run the &lt;code&gt;make testsuite&lt;/code&gt; command. When it&amp;rsquo;s finished, go to &lt;code&gt;cd /tmp/repo1&lt;/code&gt;, edit the &lt;code&gt;style.css&lt;/code&gt; file, then &lt;code&gt;git add .&lt;/code&gt; &lt;code&gt;git commit -m &amp;quot;try style&amp;quot;&lt;/code&gt; and &lt;code&gt;git push&lt;/code&gt;. Open &lt;a target=&quot;_blank&quot; href=&quot;http://127.0.0.1:4546/repo1&quot;&gt;http://127.0.0.1:4546/repo1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When you are satisfied, create a branch with your CSS in &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/resources/styles/&quot;&gt;apex&lt;/a&gt;, then &lt;code&gt;git add .&lt;/code&gt; and &lt;code&gt;git push&lt;/code&gt;. I&amp;rsquo;ll take care of the rest.&lt;/p&gt;

&lt;h3 id=&quot;i-want-to-code&quot;&gt;I want to code&lt;/h3&gt;

&lt;p&gt;Okay, no worries, here&amp;rsquo;s a list of small tasks (well, they seem small to me, but if that&amp;rsquo;s not the case for you, let me know):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;in golang:

&lt;ul&gt;
&lt;li&gt;in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/grafter/&quot;&gt;grafter&lt;/a&gt; plugin, be able to display &lt;a href=&quot;issues/3d33-grafter-dco.html&quot;&gt;something else&lt;/a&gt; in the first message of a graft. The banner is hard-coded in the plugin; it would need to be exported to the plugin configuration and used.&lt;/li&gt;
&lt;li&gt;In the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/silo/&quot;&gt;silo&lt;/a&gt; plugin, allow &lt;a href=&quot;issues/76ec-silo-more-selects.html&quot;&gt;grouping by file or date&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;More difficult, but I would like to have more &lt;a href=&quot;issues/e6d8-apex-alert-support.html&quot;&gt;renderers&lt;/a&gt; in &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/&quot;&gt;apex&lt;/a&gt;, for example info, warnings, and others&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;in typescript:

&lt;ul&gt;
&lt;li&gt;in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; plugin there is a bug in the RSS feed &lt;a href=&quot;issues/3986-pollen-rss-grow-infinite.html&quot;&gt;it is never cleaned up&lt;/a&gt;. Added an option (or not, 100 seems fine to me) to limit the number of &lt;code&gt;&amp;lt;item&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Still in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; plugin, why not add a &lt;a target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/JSON_Feed&quot;&gt;json feed&lt;/a&gt; in addition to the RSS feed? We can serialize json in assemblescript.&lt;/li&gt;
&lt;li&gt;More difficult, but it would be nice to be able to tell the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; plugin to generate an RSS feed with the contents of the files. This would allow GitRoot to have an RSS feed displaying blog articles, for example. Currently, it only puts commits in the RSS feed.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;in Rust:

&lt;ul&gt;
&lt;li&gt;Do what Ygor said in &lt;a target=&quot;_blank&quot; href=&quot;https://floss.social/@ygor/115474535138702961&quot;&gt;https://floss.social/@ygor/115474535138702961&lt;/a&gt; I&amp;rsquo;m not a Rust developer and converting &lt;code&gt;String&lt;/code&gt; to &lt;code&gt;&amp;amp;str&lt;/code&gt; doesn&amp;rsquo;t mean much to me, but if you are, I&amp;rsquo;m sure it&amp;rsquo;s a small task. The code can be found in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; and the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/hop/&quot;&gt;hop&lt;/a&gt; plugin.&lt;/li&gt;
&lt;li&gt;Add a &lt;a href=&quot; issues/9f00-rust-sdk-fake-server-test.html&quot;&gt;fakeServer&lt;/a&gt; to the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; to facilitate plugin testing. Here is the one for Go &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&quot;&gt;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;i-m-a-sysadmin&quot;&gt;I&amp;rsquo;m a sysadmin.&lt;/h3&gt;

&lt;p&gt;Perfect, there&amp;rsquo;s plenty to do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Today, there are too many steps to release GitRoot. &lt;a href=&quot;issues/dba0-simplify-release.html&quot;&gt;Automating&lt;/a&gt; as much as possible would help me a lot.&lt;/li&gt;
&lt;li&gt;If you have a little space on one of your servers, &lt;a href=&quot;doc/tutorials/deploy_instance.html&quot;&gt;try deploying an instance&lt;/a&gt; just for fun. Maybe you can improve the documentation or find something that no longer works. Or add a paragraph on using gitroot with nginx or something else.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;i-want-to-test&quot;&gt;I want to test&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;All tests are in bash in &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/testsuite/&quot;&gt;/app/testsuite/&lt;/a&gt;. The largest test is located in &lt;code&gt;runtest.sh&lt;/code&gt;, along with others.

&lt;ul&gt;
&lt;li&gt;Running them to see if they work on your system seems like a good first task to me.&lt;/li&gt;
&lt;li&gt;Creating new ones&lt;/li&gt;
&lt;li&gt;Improving existing ones by, for example, moving helper functions such as &lt;code&gt;quiet_git()&lt;/code&gt;, &lt;code&gt;report()&lt;/code&gt;, or &lt;code&gt;mySleep()&lt;/code&gt; to other files (and importing them) would be a great help to the project. You can also create new ones for &lt;code&gt;killGitroot()&lt;/code&gt;, which is done at the beginning of each file. Or &lt;code&gt;createProject()&lt;/code&gt;, which consists of going to the root repository each time and adding its name to the &lt;code&gt;.gitroot/repositories.yml&lt;/code&gt; file&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;i-just-want-to-think-discuss&quot;&gt;I just want to think/discuss&lt;/h3&gt;

&lt;p&gt;You are welcome to join the &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;gitroot channel&lt;/a&gt; on Matrix. Or contact us by email at &lt;code&gt;contact [AT] gitroot.dev&lt;/code&gt; or on the Fediverse at &lt;code&gt;@forge@gitroot.dev&lt;/code&gt; &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;on this server&lt;/a&gt;.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/how-to-help_en_11-2025.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/issues.md</link><title>Issues, Tickets, Discussions... Call them what you want!</title><pubDate>2026/06/08</pubDate><description>&lt;h1 id=&quot;issues-tickets-discussions-call-them-what-you-want&quot;&gt;Issues, Tickets, Discussions&amp;hellip; Call them what you want!&lt;/h1&gt;

&lt;p&gt;Many of you, when checking out &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev&quot;&gt;https://gitroot.dev&lt;/a&gt;, have told me that the &lt;code&gt;🐞 Issues&lt;/code&gt; button shouldn&amp;rsquo;t be labeled &amp;ldquo;Issues&amp;rdquo;, but rather &amp;ldquo;Tickets&amp;rdquo; or &amp;ldquo;Discussions&amp;rdquo;.&lt;/p&gt;

&lt;p&gt;First of all, thanks for the feedback! I really appreciate it, that&amp;rsquo;s how I can build a better forge. Of course, I will change the label, but developing a forge means having a lot of different areas to work on: issues/boards, git, code, CI/CD, and with GitRoot, even more, like plugins and WASM&amp;hellip;&lt;/p&gt;

&lt;p&gt;So, why don&amp;rsquo;t I just change this label right away? In reality, this label isn&amp;rsquo;t hardcoded inside GitRoot. It&amp;rsquo;s only there because I configured plugins to display it. I want to take this opportunity to explain how GitRoot works, because the platform is developed around this exact concept: how to customize a forge at the project level.&lt;/p&gt;

&lt;h2 id=&quot;the-original-idea&quot;&gt;The Original Idea&lt;/h2&gt;

&lt;p&gt;Imagine you are developing a forge, and to avoid vendor lock-in, you decide not to put anything outside of Git. You choose not to use any database at all. In this context, how do you design an &amp;ldquo;issue&amp;rdquo; system?&lt;/p&gt;

&lt;p&gt;In traditional forges, an &amp;ldquo;issue&amp;rdquo; consists of a title and a description created by a user. We can represent it like this:&lt;/p&gt;
&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;379.68002&quot; height=&quot;332.2&quot; viewBox=&quot;0 0 379.68002 332.2&quot;&gt;&lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;379.68002&quot; height=&quot;332.2&quot; fill=&quot;transparent&quot;/&gt;&lt;defs&gt;&lt;marker id=&quot;arrow-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 10 5 L 0 10 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-start-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;4.5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 5 L 10 10 L 10 0 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-open-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;1&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1 7 L 18 13 V 1 Z&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-open-start-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;18&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1 7 L 18 13 V 1 Z&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-dep-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;13&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 18 7 L 9 13 L 14 7 L 9 1 Z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-dep-start-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;6&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 5 7 L 9 13 L 1 7 L 9 1 Z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;path id=&quot;edge-0&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-0&quot; d=&quot;M 135.960,121.250 L 96.540,175.250&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot; x=&quot;85.37&quot; y=&quot;134.25&quot; width=&quot;61.76&quot; height=&quot;28.00&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.85&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.35&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;116.25&quot; y=&quot;151.75&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;116.25&quot; dy=&quot;0.00&quot;&gt;Author&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-1&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-1&quot; d=&quot;M 231.960,121.250 L 255.380,175.250&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-1&quot; data-label-kind=&quot;center&quot; x=&quot;217.27&quot; y=&quot;134.25&quot; width=&quot;52.80&quot; height=&quot;28.00&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.85&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.35&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-1&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;243.67&quot; y=&quot;151.75&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;243.67&quot; dy=&quot;0.00&quot;&gt;State&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;rect x=&quot;87.90&quot; y=&quot;8.00&quot; width=&quot;192.12&quot; height=&quot;113.25&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;line x1=&quot;93.90&quot; y1=&quot;46.95&quot; x2=&quot;274.02&quot; y2=&quot;46.95&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1.0&quot;/&gt;&lt;text x=&quot;183.96&quot; y=&quot;34.00&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;183.96&quot; dy=&quot;0.00&quot; font-weight=&quot;600&quot;&gt;Issue&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;117.90&quot; y=&quot;69.70&quot; text-anchor=&quot;start&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;117.90&quot; dy=&quot;0.00&quot;&gt;int id&lt;/tspan&gt;&lt;tspan x=&quot;117.90&quot; dy=&quot;17.85&quot;&gt;string title&lt;/tspan&gt;&lt;tspan x=&quot;117.90&quot; dy=&quot;17.85&quot;&gt;string description&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;203.08&quot; y=&quot;175.25&quot; width=&quot;168.60&quot; height=&quot;148.95&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;line x1=&quot;209.08&quot; y1=&quot;214.20&quot; x2=&quot;365.68&quot; y2=&quot;214.20&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1.0&quot;/&gt;&lt;text x=&quot;287.38&quot; y=&quot;201.25&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;287.38&quot; dy=&quot;0.00&quot; font-weight=&quot;600&quot;&gt;Status&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;233.08&quot; y=&quot;236.95&quot; text-anchor=&quot;start&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;0.00&quot;&gt;&amp;lt;&amp;lt;enumeration&amp;gt;&amp;gt;&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;Open&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;TODO&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;DOING&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;CLOSE&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;8.00&quot; y=&quot;175.25&quot; width=&quot;145.08&quot; height=&quot;95.40&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;line x1=&quot;14.00&quot; y1=&quot;214.20&quot; x2=&quot;147.08&quot; y2=&quot;214.20&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1.0&quot;/&gt;&lt;text x=&quot;80.54&quot; y=&quot;201.25&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;80.54&quot; dy=&quot;0.00&quot; font-weight=&quot;600&quot;&gt;User&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;38.00&quot; y=&quot;236.95&quot; text-anchor=&quot;start&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;38.00&quot; dy=&quot;0.00&quot;&gt;int id&lt;/tspan&gt;&lt;tspan x=&quot;38.00&quot; dy=&quot;17.85&quot;&gt;string email&lt;/tspan&gt;&lt;/text&gt;&lt;/svg&gt;
&lt;p&gt;Easy, right? Well, how do you represent that in GitRoot using only files? This question was running through my mind for months before I finally found a solution.&lt;/p&gt;

&lt;h2 id=&quot;the-basic-solution&quot;&gt;The Basic Solution&lt;/h2&gt;

&lt;p&gt;Since I only have files at my disposal, what actually constitutes an &amp;ldquo;issue&amp;rdquo;? I need to add some metadata to a file so the system understands that this file is an issue and not, say, a &lt;code&gt;main.go&lt;/code&gt;. I’ll probably want to group all these &amp;ldquo;issues&amp;rdquo; into a specific directory, because while GitRoot technically lets you do whatever you want (even dropping an issue right in the middle of your source code), I personally prefer to keep my files organized!&lt;/p&gt;

&lt;p&gt;A simple text format that handles metadata perfectly is Markdown. I use Markdown here, but any other markup language could work. Let&amp;rsquo;s create a file named &lt;code&gt;issues/1.md&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;id: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;user: email@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;status: OPEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# My title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt;9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;My description where I can detail what my problem is.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yes, it really is that simple. In GitRoot, to create an issue, you just create a file, add some metadata, commit, and push. Boom, you have an issue.&lt;/p&gt;

&lt;p&gt;Now, you might be thinking: &amp;ldquo;Well, I lose a lot of control with that, the status could be anything!&amp;rdquo; or &amp;ldquo;How are other users supposed to know what to put where?&amp;rdquo;. You&amp;rsquo;re right, and that is exactly where plugins enter the game.&lt;/p&gt;

&lt;h2 id=&quot;a-better-solution&quot;&gt;A Better Solution&lt;/h2&gt;

&lt;p&gt;If I simplify this as much as possible, what does an external contributor actually need to provide to create an issue? Just a title and a description. So, let&amp;rsquo;s create a file named &lt;code&gt;issues/my_issue.md&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# My title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;My description where I can detail what my problem is.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we run &lt;code&gt;git commit &amp;amp;&amp;amp; git push&lt;/code&gt;. Since I have configured the &lt;a href=&quot;plugins/name/ladybug.html&quot;&gt;ladybug plugin&lt;/a&gt; like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: ladybug
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: issues/**/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;func&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: issues/**/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;callfunc&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;default&lt;/span&gt;: autogenerated
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;mandatory&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: id
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;type&lt;/span&gt;: crc16
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;default&lt;/span&gt;: OPEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;mandatory&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: status
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;type&lt;/span&gt;: string
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;default&lt;/span&gt;: email@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;mandatory&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: user
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;type&lt;/span&gt;: string
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can read this configuration as: &amp;ldquo;For every Markdown file in the &lt;code&gt;issues&lt;/code&gt; directory across all branches, if the file doesn&amp;rsquo;t have these metadata fields, automatically add these default values.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, if you are a Git purist, a server creating commits right on top of yours might sound a bit radical, or even terrifying. But look closely at the &lt;code&gt;write&lt;/code&gt; block in the YAML configuration above:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: issues/**/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;GitRoot doesn&amp;rsquo;t just let plugins run wild, the project configuration must explicitly grant the plugin permission to write or modify files on specific paths. If you don&amp;rsquo;t declare these rights, GitRoot will block the plugin from making any changes.&lt;/p&gt;

&lt;p&gt;When granted, GitRoot intercepts your push, lets the plugin inject the missing metadata, and safely generates a follow-up commit. And don&amp;rsquo;t worry about your history: if you decide to &lt;code&gt;git rebase -i&lt;/code&gt; later to squash or fixup your commits, plugins won&amp;rsquo;t generate duplicate commits if no metadata has changed.&lt;/p&gt;

&lt;p&gt;What about security or external users trying to forge metadata? Since contributors cannot push directly to the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt; (like &lt;code&gt;main&lt;/code&gt;), they have to go through a branch and need to be merged. This means every single change to an issue&amp;rsquo;s status or metadata is subjected to a code review.&lt;/p&gt;

&lt;p&gt;If an external user tries to close someone else&amp;rsquo;s issue or manipulate IDs, it will stand out like a sore thumb in the git diff. Maintainers retain absolute control and can simply reject the rogue contribution. Security doesn&amp;rsquo;t rely on complex database permission tables, but on the battle-tested git review workflow you already know and trust.&lt;/p&gt;

&lt;p&gt;So, once you push, the server-side plugin does its job, and the next time you run &lt;code&gt;git pull&lt;/code&gt;, your local file will automatically look like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;id: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;user: email@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;status: OPEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# My title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt;9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;My description where I can detail what my problem is.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bingo! You get the exact same file, with the exact same metadata you wanted. Of course, a lot more needs to be added, like defining a &lt;code&gt;type: user&lt;/code&gt; to assign an issue, or perhaps a &lt;code&gt;type: status&lt;/code&gt; to define an enum of allowed values. But I think you see where I&amp;rsquo;m going with this feature!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: If you&amp;rsquo;re wondering about the barrier to entry for non-technical contributors, my long-term vision is to allow plugins to intercept standard HTTP requests. Tomorrow, a classic web form will be able to POST to GitRoot and automatically commit a &lt;code&gt;.md&lt;/code&gt; file under the hood. But for now, we are proudly dev-centric!)&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;so-what-about-the-label&quot;&gt;So, What About the Label?&lt;/h2&gt;

&lt;p&gt;Throughout all of this, you’ll notice that the term &amp;ldquo;issue&amp;rdquo; only appears within the plugins themselves. Nowhere have we talked about that &lt;code&gt;🐞 Issues&lt;/code&gt; button yet. We actually need to introduce another plugin before we can tackle that topic.&lt;/p&gt;

&lt;p&gt;First, we need a file that lists all of these &amp;ldquo;issues&amp;rdquo;. That’s exactly why the &lt;a href=&quot;plugins/name/silo.html&quot;&gt;silo plugin&lt;/a&gt; exists. It inspects all files and groups them whenever it finds a matching pattern.&lt;/p&gt;

&lt;p&gt;To list all open &amp;ldquo;issues&amp;rdquo;, we configure it like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: silo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;func&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: boards/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - append
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;callfunc&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;boards&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;description&lt;/span&gt;: All open issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;for&lt;/span&gt;: issues/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;format&lt;/span&gt;: table
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;paginator&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;50&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;selects&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;status: (.*)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;user: (.*)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;sort&lt;/span&gt;: select[0]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;sortOrder&lt;/span&gt;: desc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;tableHeader&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;| | status | user |&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;title&lt;/span&gt;: All issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;to&lt;/span&gt;: boards/issues.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;where&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;status: OPEN&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can read this configuration as: &amp;ldquo;For every file in the main branch, create a board called &amp;lsquo;All issues&amp;rsquo; containing links to all files matching &lt;code&gt;status: OPEN&lt;/code&gt;. Display them as a table with &lt;code&gt;status&lt;/code&gt; and &lt;code&gt;user&lt;/code&gt; columns.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After running &lt;code&gt;git commit &amp;amp;&amp;amp; git push &amp;amp;&amp;amp; git pull&lt;/code&gt;, you will get a new file at &lt;code&gt;boards/issues.md&lt;/code&gt; containing something like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# All issues
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;All open issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;|                                   | status | user            |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;| --------------------------------- | ------ | --------------- |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;| [&lt;span style=&quot;color:#ff79c6&quot;&gt;My title&lt;/span&gt;](&lt;span style=&quot;color:#50fa7b&quot;&gt;../issues/my_issue.md&lt;/span&gt;) | OPEN   | email@email.com |
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Silo has aggregated all your open issues into a single dashboard. Of course, you could easily create another board for closed issues in a separate file like &lt;code&gt;boards/closed.md&lt;/code&gt;, it&amp;rsquo;s entirely up to you.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: If you are sweating at the thought of a plugin scanning your entire repository on every single push, don&amp;rsquo;t panic. &lt;code&gt;silo&lt;/code&gt;, thanks to the plugin-sdk API, doesn&amp;rsquo;t do a full disk scan. It only processes the incremental diffs generated by the incoming commit. This is precisely why we restrict its scope to the &lt;code&gt;main&lt;/code&gt; branch in this example.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now that we have a page representing all our issues, we can finally render it in the browser. Because, as you might have guessed, the label causing all this debate lives in the Web UI.&lt;/p&gt;

&lt;h2 id=&quot;the-web-is-where-it-happens&quot;&gt;The Web is Where It Happens&lt;/h2&gt;

&lt;p&gt;To render my &lt;code&gt;boards/issues.md&lt;/code&gt; file on the web, I use the &lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex&lt;/a&gt; plugin, in conjunction with the &lt;a href=&quot;plugins/name/apex_markdown.html&quot;&gt;apex_markdown&lt;/a&gt; plugin, of course.&lt;/p&gt;

&lt;p&gt;These plugins take files from git and render them directly in the Web UI. I configure them like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: apex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;func&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - append
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - append
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;callfunc&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;pluginname&lt;/span&gt;: apex_markdown
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;funcname&lt;/span&gt;: renderMd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;menu&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 🏠 Home
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 📖 Documentation
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L38&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L38&quot;&gt;38&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /doc/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L39&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L39&quot;&gt;39&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 🔖 Versions
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L40&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L40&quot;&gt;40&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /CHANGELOG.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L41&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L41&quot;&gt;41&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: �� Plugins
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L42&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L42&quot;&gt;42&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /plugins/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L43&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L43&quot;&gt;43&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: �� Issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L44&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L44&quot;&gt;44&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /boards/issues.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L45&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L45&quot;&gt;45&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 🚀 Code
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L46&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L46&quot;&gt;46&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /worktree/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L47&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L47&quot;&gt;47&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: ☕ Blog
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L48&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L48&quot;&gt;48&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /blog/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L49&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L49&quot;&gt;49&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: �� Contact
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L50&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L50&quot;&gt;50&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /contact.html
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oh, do you see it now? Yes, right there: &lt;code&gt;display: 🐞 Issues&lt;/code&gt;. This is the exact configuration that tells Apex to add a global menu item with the &lt;code&gt;🐞 Issues&lt;/code&gt; label, pointing to the &lt;code&gt;/boards/issues.html&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;If you are wondering how Apex magically knows where to find your &amp;ldquo;issues&amp;rdquo;, the secret is: it doesn&amp;rsquo;t.&lt;/p&gt;

&lt;p&gt;Apex isn&amp;rsquo;t a smart router, it&amp;rsquo;s just a file translator. It blindly takes the Markdown structure generated by GitRoot and renders it into HTML. Because I know Apex transforms &lt;code&gt;boards/issues.md&lt;/code&gt; into &lt;code&gt;boards/issues.html&lt;/code&gt;, I simply hardcode that target URL in my menu configuration (&lt;code&gt;link: /boards/issues.html&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;And what about the links inside the table? Apex has a clever built-in rewrite engine. When it parses your generated board file, it automatically rewrites every internal Markdown link, turning &lt;code&gt;[My title](../issues/my_issue.md)&lt;/code&gt; into a standard web-friendly &lt;code&gt;&amp;lt;a href=&amp;quot;../issues/my_issue.html&amp;quot;&amp;gt;&lt;/code&gt;. No database, no dynamic routing, just plain old static files mapped one-to-one.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;If you change the label to &lt;code&gt;🐞 Tickets&lt;/code&gt; or &lt;code&gt;💡 Discussions&lt;/code&gt;, the &amp;ldquo;issue to rename issues into something else&amp;rdquo; can finally be closed. Ideally, you might also want to rename some directories in the other plugins&amp;rsquo; configurations, but it&amp;rsquo;s really not a big deal.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;m pretty sure you now understand why I didn&amp;rsquo;t immediately jump on resolving this task: everything we just saw is pure configuration, completely separated from the GitRoot core code.&lt;/p&gt;

&lt;p&gt;And to be perfectly honest, I haven&amp;rsquo;t changed it yet because none of the traditional options really appeal to me. I don&amp;rsquo;t particularly like &amp;ldquo;Tickets&amp;rdquo; or &amp;ldquo;Discussions&amp;rdquo;, maybe it should be &amp;ldquo;Feature Requests&amp;rdquo; instead (and yes, you can now model that in GitRoot too!).&lt;/p&gt;

&lt;p&gt;In fact, I personally call them &lt;code&gt;TODO&lt;/code&gt;s, because in reality, they are all just waiting for me to get them done.&lt;/p&gt;

&lt;h3 id=&quot;wait-why-stop-at-issues&quot;&gt;Wait&amp;hellip; Why stop at issues?&lt;/h3&gt;

&lt;p&gt;This is where the true power of GitRoot will blow your mind.&lt;/p&gt;

&lt;p&gt;Because GitRoot doesn&amp;rsquo;t care about databases, and because everything is just a file inside Git, the &lt;code&gt;silo&lt;/code&gt; plugin can aggregate anything. I don&amp;rsquo;t need a separate &lt;code&gt;TODO&lt;/code&gt; page for my tickets, because I already have one that automatically tracks the &lt;code&gt;//TODO&lt;/code&gt; comments inside my actual source code.&lt;/p&gt;

&lt;p&gt;Yes, you read that right. You can visit it live at &lt;a href=&quot;boards/todos.html&quot;&gt;https://gitroot.dev/boards/todos.html&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Every time I write a &lt;code&gt;//TODO&lt;/code&gt; in a &lt;code&gt;.go&lt;/code&gt;, &lt;code&gt;.rs&lt;/code&gt; or &lt;code&gt;.ts&lt;/code&gt; file, GitRoot parses it, aggregates it, and generates a dynamic dashboard. Tickets, documentation, source code comments&amp;hellip; it&amp;rsquo;s all the exact same data structure.&lt;/p&gt;

&lt;p&gt;To be clear, it doesn’t magically transform your source code comments into full-blown database-like issues with IDs and statuses. It simply extracts them to give you a centralized, living view of your technical debt, right alongside your official tickets. It’s the exact same data aggregation pipeline, tailored for two different use cases.&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to discover more plugins and configurations, feel free to browse through the &lt;a href=&quot;plugins&quot;&gt;🧩 Plugins section&lt;/a&gt;. However, for now, the best way to explore remains checking out the actual &lt;a href=&quot;.gitroot/plugins.yml&quot;&gt;GitRoot plugins.yml configuration file&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
</description><guid>https://gitroot.dev//blog/issues.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.5.0-plugins.md</link><title>0.5.0 plugins</title><pubDate>2026/10/30</pubDate><description>&lt;h1 id=&quot;what-is-gitroot-without-its-plugins&quot;&gt;What is GitRoot without its plugins?&lt;/h1&gt;

&lt;p&gt;In the &lt;a href=&quot;blog/version-0.5.0.html&quot;&gt;release 0.5.0 blog post&lt;/a&gt; I have only talked about the GitRoot core code. But GitRoot is a forge where plugins take a lot of space. In the future I want to decouple GitRoot and its plugins. I hope to stabilize the plugin-sdk, release them, so that you will be able to create your own plugins.&lt;/p&gt;

&lt;p&gt;But today, GitRoot and &lt;a href=&quot;plugins/scope_gitroot.html&quot;&gt;officials plugins&lt;/a&gt; are linked by the version. So let me explain what they can do in this new version.&lt;/p&gt;

&lt;h2 id=&quot;all-plugins-are-compatible-with-the-0-5-0&quot;&gt;All plugins are compatible with the 0.5.0&lt;/h2&gt;

&lt;p&gt;All plugins are compatible with the 0.5.0 plugin-sdk. What does that mean? Every time you see a &lt;strong&gt;BREAKING&lt;/strong&gt; and &lt;code&gt;plugin&lt;/code&gt; in the &lt;a href=&quot;CHANGELOG.html&quot;&gt;changelog&lt;/a&gt;, it means I had to change the code of the plugin-sdk and recompile plugins with that new code (sometimes I need to change the code of the plugin, sometimes not). So when you see &lt;code&gt;compatible with gitroot plugin-sdk 0.5.0&lt;/code&gt; in the changelog of a plugin, it means that the plugin is good to run inside this GitRoot version.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ll present here only the most important features, for details you can read the changelog of each plugin.&lt;/p&gt;

&lt;h2 id=&quot;firefly-in-zig&quot;&gt;Firefly in zig&lt;/h2&gt;

&lt;p&gt;First, let me introduce &lt;a href=&quot;plugins/name/firefly.html&quot;&gt;Firefly&lt;/a&gt;: add URLs to call and Firefly will generate beautiful status pages for them. Yes, it&amp;rsquo;s a status page directly integrated into your forge, so no need to depend on a third-party service. Of course, it&amp;rsquo;s useful only if the app observed is not on the same host as your forge. So it&amp;rsquo;s totally useless for GitRoot. But as an example, you can see the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/status/&quot;&gt;status page of gitroot on gitroot&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://gitroot.dev/branches/v0.5-plugins/blog/version-0.5.0-plugins-firefly.png&quot; alt=&quot;Screenshot of the firefly plugin, it&amp;rsquo;s a status plugin, we see gitroot.dev is up and running, also @forge@gitroot.dev (gotosocial of gitroot) is running.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;m proud because it&amp;rsquo;s my first code in the &lt;a target=&quot;_blank&quot; href=&quot;https://ziglang.org/&quot;&gt;zig&lt;/a&gt; language. Not everything is perfect, of course, but I love the language itself, so I will make other plugins and rework them when needed. If you are Zig lovers too, don&amp;rsquo;t hesitate to help me.&lt;/p&gt;

&lt;h2 id=&quot;silo-lots-of-new-things&quot;&gt;Silo, lots of new things&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;plugins/name/silo.html&quot;&gt;Silo&lt;/a&gt;: from files and metadata, Silo generates indexes to find files more easily. I have added a lot of features into this milestone.&lt;/p&gt;

&lt;p&gt;You can now &lt;code&gt;sort&lt;/code&gt; the rendered list by the files which each item links to. I use it to render all the &lt;code&gt;TODO&lt;/code&gt;s in GitRoot, &lt;a href=&quot;boards/todos.html&quot;&gt;in the todo board&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now Silo can render the list directly to the web. Useful to not encumber the git repositories with redundant lists. For example, I use it to render plugins in &lt;a href=&quot;plugins&quot;&gt;alphabetical order&lt;/a&gt; or by &lt;a href=&quot;plugins/scope_gitroot.html&quot;&gt;scope&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Furthermore, as you can see in the &lt;a href=&quot;plugins&quot;&gt;plugins page&lt;/a&gt;, Silo can now render lists by alphabetical group. To render them, I have added a new property &lt;code&gt;groupBy&lt;/code&gt; which allows you to tell Silo which property you want to group your list items by.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;groupBy&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;desciption&lt;/code&gt; and &lt;code&gt;to&lt;/code&gt; properties can contain &lt;code&gt;$variable&lt;/code&gt;. These variables will be interpreted when Silo renders the list. So, for example, if you want to render the title of a list with a special metadata of the content, you can:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;selects&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;purl: pkg:gitroot/[^/]+/([^@]+)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;purl: pkg:gitroot/([^/]+)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;title&lt;/span&gt;: Plugins for scope $select[1]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means that you select the scope and name of the PURL plugin and render the name into the title. A way to add metadata into the rendered list is to use the new &lt;code&gt;metadata&lt;/code&gt; property.&lt;/p&gt;
&lt;blockquote style=&quot;border-color: #006ac7&quot;&gt;

&lt;p&gt;The silo plugin use the &lt;a target=&quot;_blank&quot; href=&quot;https://pkg.go.dev/regexp&quot;&gt;standard regexp&lt;/a&gt; from go to extract selects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With all of that, dynamic boards can be created. From files to index files with metadata, you can create indexes of indexes. An example is the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/plugins/scope.html&quot;&gt;scope page&lt;/a&gt;. Silo takes all plugins from the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/plugins/name/index.html&quot;&gt;plugins/name directory&lt;/a&gt;, generates a page which is an index of all scopes, and then generates a page for each. Today only the &lt;a href=&quot;plugins/scope_gitroot.html&quot;&gt;gitroot scope&lt;/a&gt; exists, but I hope for a lot more in the future.&lt;/p&gt;

&lt;p&gt;Not related, but a good addition to the Silo plugin is the &lt;code&gt;custom&lt;/code&gt; data format. It permits to render each element in the format you want (by default, only list and table were possible). I use it to render the &lt;a href=&quot;blog&quot;&gt;blog posts index&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote style=&quot;border-color: #006ac7&quot;&gt;

&lt;p&gt;To find examples on how to configure all of that, look at the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/.gitroot/plugins.yml.html#L60&quot;&gt;silo configuration&lt;/a&gt; of gitroot.dev instance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;apex-is-the-web-view&quot;&gt;Apex is the web view&lt;/h2&gt;

&lt;p&gt;A lot of tiny, but useful things have been added to the &lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex&lt;/a&gt; plugin. Apex is the plugin that renders the files from your git repository to the web. You configure the &lt;code&gt;header&lt;/code&gt;, &lt;code&gt;footer&lt;/code&gt;, &lt;code&gt;menu&lt;/code&gt;, and it takes care of assembling all of that with your &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; content. Today it only renders HTML and markdown (through the &lt;a href=&quot;plugins/name/apex_markdown.html&quot;&gt;apex_markdown plugin&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;So, the first addition is to export a &lt;code&gt;renderHtml&lt;/code&gt; function. With that in place, all other plugins can render something with your header, menu, footer and the right CSS. Without it, most plugins rendered only the &lt;code&gt;content&lt;/code&gt; of the page withtout &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;. For example, see what Firefly looks like without Apex:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://gitroot.dev/branches/v0.5-plugins/blog/version-0.5.0-plugins-firefly-no-apex.png&quot; alt=&quot;Screenshot of firefly plugin without css, so mostly black on white page&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A new &lt;code&gt;layout&lt;/code&gt; has been added: &lt;code&gt;slide&lt;/code&gt;. Previously, the property &lt;code&gt;layout&lt;/code&gt; could be filled with a path and Apex used it (if it didn&amp;rsquo;t exist, Apex created it with the default one inside). But if instead of a path you set &lt;code&gt;issue&lt;/code&gt;, Apex will render all matching paths with a particular layout: see &lt;a href=&quot;issues/close/v0.5.0/88d7-anonymous-can-create-branch.html&quot;&gt;any issue&lt;/a&gt;, Apex has added metadata before the page, and grouped all content inside an &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; instead of a &lt;code&gt;&amp;lt;br&amp;gt;&lt;/code&gt; like you can see in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/issues/close/v0.5.0/88d7-anonymous-can-create-branch.md.html&quot;&gt;original markdown page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Following this strategy, the new &lt;code&gt;slide&lt;/code&gt; layout renders the page with the &lt;a href=&quot;app/plugins/apex/resources/styles/slide.css&quot;&gt;slide.css&lt;/a&gt; and groups all page breaks into &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; as required by the CSS.&lt;/p&gt;

&lt;p&gt;If Apex renders markdown through the apex_markdown plugin, how is that possible? All the magic resides in a new property added in the apex_markdown plugin. This property is &lt;code&gt;horizontalRuleMetadatas&lt;/code&gt;, with that, Apex can instruct the markdown renderer how to render page breaks.&lt;/p&gt;

&lt;p&gt;Another addition to apex_markdown is the ability to render callouts for blockquotes and details/summary. To render a callout, use this in your markdown:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;text-decoration:underline&quot;&gt;[!NOTE]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;text-decoration:underline&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;text-decoration:underline&quot;&gt;This a note callout.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which renders on the web as:&lt;/p&gt;
&lt;blockquote style=&quot;border-color: #006ac7&quot;&gt;

&lt;p&gt;This a note callout.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Today, the possibilities are (but in the future I want to make them configurable):&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;var&lt;/span&gt; calloutColor = &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;map&lt;/span&gt;[&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;]&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;[!NOTE]&amp;#34;&lt;/span&gt;:    &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;#006ac7&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;[!WARNING]&amp;#34;&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;#be4700&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;[!ERROR]&amp;#34;&lt;/span&gt;:   &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;#f00&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Following the same philosophy, to render details/summary:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;text-decoration:underline&quot;&gt;[!DETAILS] This is a summary
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;text-decoration:underline&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;text-decoration:underline&quot;&gt;This is the details
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which renders on the web as:&lt;/p&gt;
&lt;details&gt;&lt;summary&gt;This is a summary&lt;/summary&gt;

&lt;p&gt;This is the details&lt;/p&gt;
&lt;/details&gt;

&lt;p&gt;Finally, in a last-minute change before the release, I have added the metadata &lt;code&gt;toc: true&lt;/code&gt; to be able to render a table of contents, because the &lt;a href=&quot;blog/version-0.5.0.html&quot;&gt;v0.5.0 article&lt;/a&gt; was too long.&lt;/p&gt;

&lt;h2 id=&quot;grafter-diff&quot;&gt;Grafter diff&lt;/h2&gt;

&lt;p&gt;An important (in UX, not size) change in the &lt;a href=&quot;plugins/name/grafter.html&quot;&gt;grafter plugin&lt;/a&gt; is that it now renders commit diffs from the start. Grafter is the plugin responsible for managing contributions, it permits discussing what has been done and finally merging (or not) the contribution.&lt;/p&gt;

&lt;p&gt;Previously, Grafter displayed diffs only after the property &lt;code&gt;status&lt;/code&gt; was set to &lt;code&gt;review&lt;/code&gt;. But that was confusing. Now, from the start, it shows diffs.&lt;/p&gt;

&lt;p&gt;And now that the Apex plugin can render summary/details, Grafter uses it to render reports from other plugins. You can see the result in the graft &lt;a href=&quot;grafts/v0.5-plugins.html&quot;&gt;of this post&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;pollen-render-content&quot;&gt;Pollen render content&lt;/h2&gt;

&lt;p&gt;If you use &lt;a href=&quot;plugins/name/pollen.html&quot;&gt;pollen&lt;/a&gt;, you can delete the property &lt;code&gt;link&lt;/code&gt; as it is calculated directly from the forge configuration. Pollen is the plugin responsible for communicating with the outside of your forge. Today it uses only RSS.&lt;/p&gt;

&lt;p&gt;But the biggest change is the ability to render the content of files in addition to the commits. Previously, Pollen rendered only commit metadata (title, description, author&amp;hellip;). Now it can render the content of files to have a true RSS for your issues, grafts, or even blog posts. Don&amp;rsquo;t forget to subscribe to the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/rss/blog.xml&quot;&gt;blog rss&lt;/a&gt; of this instance.&lt;/p&gt;

&lt;h2 id=&quot;what-next&quot;&gt;What next?&lt;/h2&gt;

&lt;p&gt;One big thing I wanted to do before the &lt;a href=&quot;blog/version-0.5.0.html&quot;&gt;0.5.0 release&lt;/a&gt; (but ran out of time) was to change how Apex renders links. Today all links are absolute with a trick in the html page (&lt;code&gt;&amp;lt;base href=&amp;quot;https://gitroot.dev/&amp;quot;&amp;gt;&lt;/code&gt;), I have done that because the &lt;a href=&quot;blog/no-existing.html&quot;&gt;404 page&lt;/a&gt; needs to be rendered everywhere in the repository. Unfortunately, that makes too many bugs with bad linking. So I want to revert that as priority one (maybe keep the trick only for the 404 page).&lt;/p&gt;

&lt;p&gt;After that, I want to better render Git in the web. A lot of missing concepts need to be rendered, &lt;code&gt;commit&lt;/code&gt;, &lt;code&gt;branch&lt;/code&gt; are the first ones. So I will add an &lt;code&gt;apex_git&lt;/code&gt; plugin. And if I have time, an &lt;code&gt;apex_gitroot&lt;/code&gt; plugin to render specific GitRoot files like &lt;code&gt;users.yml&lt;/code&gt;, &lt;code&gt;plugins.yml&lt;/code&gt; or &lt;code&gt;repositories.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I have grouped multiple related issues in the &lt;a href=&quot;boards/status_apex_git.html&quot;&gt;apex board&lt;/a&gt;. That will be my priority, but of course, as I change my mind a lot, that&amp;rsquo;s not a promise of what will be done.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Special thanks to &lt;code&gt;Ryuno-Ki&lt;/code&gt; from the &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/#gitroot:matrix.org&quot;&gt;#gitroot:matrix.org&lt;/a&gt; Matrix room for reviewing and correcting this article. Want to help with future posts? Join us in the room!&lt;/em&gt;&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.5.0-plugins.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.5.0.md</link><title>0.5.0</title><pubDate>2026/10/05</pubDate><description>&lt;nav&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#move-slowly-and-take-care-of-things&quot;&gt;Move slowly, and take care of things&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#breaking-purl&quot;&gt;Breaking PURL&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#a-lot-of-people-have-discovered-gitroot&quot;&gt;A lot of people have discovered GitRoot&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#not-conclusive&quot;&gt;Not conclusive&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#before-it-s-too-late&quot;&gt;Before it&amp;rsquo;s too late&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#my-new-favorite-language-until-the-next-one&quot;&gt;My new favorite language&amp;hellip;. until the next one&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#plugins-needs-more-power&quot;&gt;Plugins needs more power&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#sftp-to-save-the-web&quot;&gt;Sftp to save the web&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#how-many-bugs&quot;&gt;How many bugs?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#in-depth-details&quot;&gt;In-depth Details&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#purl-breaking-details&quot;&gt;PURL breaking details&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#gitroot-communication&quot;&gt;GitRoot communication&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#sdkgenerator-full-story&quot;&gt;SdkGenerator full story&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#max-anonymous-branches&quot;&gt;Max anonymous branches&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#zig-plugin-sdk&quot;&gt;Zig plugin-sdk&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#pluginsdk-improvements&quot;&gt;PluginSdk improvements&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#sftp-feature&quot;&gt;Sftp feature&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#some-interesting-bug-fixes&quot;&gt;Some interesting bug fixes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#what-next&quot;&gt;What next?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;/nav&gt;

&lt;h1 id=&quot;move-slowly-and-take-care-of-things&quot;&gt;Move slowly, and take care of things&lt;/h1&gt;

&lt;p&gt;These days, everything moves too fast. They push thousands of lines of AI generated code every hour, deploying it without even understanding how it works. But with GitRoot, I want to do the exact opposite. I want to take my time, savor every architectural choice, and thoroughly enjoy writing every line of code by hand. It&amp;rsquo;s a project that is deeply close to my heart: a space for freedom far away from the rat race of metrics. So yes, we move at our own pace: &lt;code&gt;slowly, and take care of things&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote style=&quot;border-color: #006ac7&quot;&gt;

&lt;p&gt;Thanks to &lt;a target=&quot;_blank&quot; href=&quot;https://pouet.chapril.org/@lonugem&quot;&gt;lonugem&lt;/a&gt; for the title idea&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes&lt;/h2&gt;

&lt;h3 id=&quot;breaking-purl&quot;&gt;Breaking PURL&lt;/h3&gt;

&lt;p&gt;I&amp;rsquo;m sorry to say that the first feature I want to talk about is a big breaking one. I know, I know, on the homepage of GitRoot I say &amp;ldquo;no headaches to upgrade my forge&amp;rdquo;&amp;hellip;. but this time you will&amp;hellip; If you are not happy, you can contact the support and get a refund for the previous year and a 100% discount for the next one :D&lt;/p&gt;

&lt;p&gt;Joking aside, you need to replace your plugin URL with a PURL one. &lt;a target=&quot;_blank&quot; href=&quot;https://ecma-international.org/publications-and-standards/standards/ecma-427/&quot;&gt;ECMA PURL&lt;/a&gt; is a specification to produce &amp;ldquo;a standardized URL-based syntax that uniquely identifies software packages&amp;rdquo;.&lt;/p&gt;

&lt;p&gt;This corresponds to the following change:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#f55&quot;&gt;- url: https://gitroot.dev/releases/0.4.0/ladybug-0.0.4.wasm
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#f55&quot;&gt;- checksum: sha256:7409837469ce89d5c9fd8dd48236764650f48523ff4d81295967154977a01635
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#f55&quot;&gt;- name: ladybug
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#f55&quot;&gt;- version: 0.0.4
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#f55&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#50fa7b;font-weight:bold&quot;&gt;+ purl: pkg:gitroot/gitroot/ladybug@0.0.5?checksum=sha256:afb22a26e7d489d72db0a2305d433a3c3ac2692959ba2412011c0d759b4c8518&amp;amp;registry=https:%2F%2Fgitroot.dev%2Freleases%2F0.5.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#50fa7b;font-weight:bold&quot;&gt;&lt;/span&gt;  active: true
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;GitRoot will download &lt;code&gt;$registry/$scope-$name-$version.wasm&lt;/code&gt;, so it will be &lt;code&gt;https://gitroot.dev/releases/0.5.0/gitroot-ladybug-0.0.5.wasm&lt;/code&gt; for the example. And then compare the downloaded WASM binary with the checksum provided in the PURL.&lt;/p&gt;
&lt;blockquote style=&quot;border-color: #be4700&quot;&gt;

&lt;p&gt;Look in the &lt;a href=&quot;plugins/index.html&quot;&gt;plugins registry&lt;/a&gt; to find the exact purl for each plugin.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To know why, how and more &lt;a href=&quot;blog/version-0.5.0.html#purl-breaking-details&quot;&gt;read purl details&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;a-lot-of-people-have-discovered-gitroot&quot;&gt;A lot of people have discovered GitRoot&lt;/h3&gt;

&lt;p&gt;First show, first lobste.rs/HN links, first video&amp;hellip; Yes, more and more people are talking about GitRoot. &lt;a href=&quot;blog/version-0.5.0.html#gitroot-communication&quot;&gt;Discover where&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;not-conclusive&quot;&gt;Not conclusive&lt;/h3&gt;

&lt;p&gt;Sometimes you spend more time on a subject than you thought. And sometimes, after all of this time spent, you don&amp;rsquo;t have what you wanted to have at the start&amp;hellip; That is the case for the &lt;code&gt;sdkgenerator&lt;/code&gt; experiment. It should &amp;ldquo;just&amp;rdquo; generate Rust, TS, Zig code from Golang&amp;hellip; &amp;ldquo;Just&amp;rdquo;, lol, I never finished it. Maybe someone wants help? &lt;a href=&quot;blog/version-0.5.0.html#sdkgenerator-full-story&quot;&gt;Read the full sdkgenerator story&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;before-it-s-too-late&quot;&gt;Before it&amp;rsquo;s too late&lt;/h3&gt;

&lt;p&gt;How to prevent spam with a forge without registration? This question was asked by NLnet 2 years ago (no, they don&amp;rsquo;t fund GitRoot, I was rejected). Two years later I have the answer, not perfect, but it&amp;rsquo;s a start. I added two new properties: one in &lt;code&gt;.gitroot/forgeConf.yml&lt;/code&gt; of your root repository, &lt;code&gt;NbMaxBranchesPerProjectBeforeRejectingAnonymous&lt;/code&gt;, which is the number of branches before rejecting anonymous pushes. And one in &lt;code&gt;.gitroot/repoConfiguration.yml&lt;/code&gt; of all your repositories, &lt;code&gt;NbMaxBranchesBeforeRejectingAnonymous&lt;/code&gt;, which serves the same purpose but for a single project. &lt;a href=&quot;blog/version-0.5.0.html#max-anonymous-branches&quot;&gt;Read why and see the full example&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;my-new-favorite-language-until-the-next-one&quot;&gt;My new favorite language&amp;hellip;. until the next one&lt;/h3&gt;

&lt;p&gt;After Golang, AssemblyScript, and Rust, the plugin-sdk is now accessible for the Zig language. I continue to add new languages and discover how they work. This one was especially refreshing in its implementation. &lt;a href=&quot;blog/version-0.5.0.html#zig-plugin-sdk&quot;&gt;Read more&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;plugins-needs-more-power&quot;&gt;Plugins needs more power&lt;/h3&gt;

&lt;p&gt;Talking about the plugin-sdk, they all receive a lot of new features. They can now &lt;code&gt;schedule&lt;/code&gt; jobs, make &lt;code&gt;http&lt;/code&gt; requests, and configure plugins during &lt;code&gt;CallFunc&lt;/code&gt;. I have also simplified the initialization part. &lt;a href=&quot;blog/version-0.5.0.html#pluginsdk-improvements&quot;&gt;Read details&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;sftp-to-save-the-web&quot;&gt;Sftp to save the web&lt;/h3&gt;

&lt;p&gt;How to add images to the web without cluttering Git? That was the problem I had to tackle before adding slideshows to GitRoot. SFTP came to the rescue, and with ~200 lines of code, it&amp;rsquo;s now possible. I love when a simple, established protocol allows you to fix a complicated feature. &lt;a href=&quot;blog/version-0.5.0.html#sftp-feature&quot;&gt;Read more&lt;/a&gt; to know how to enable it.&lt;/p&gt;

&lt;h3 id=&quot;how-many-bugs&quot;&gt;How many bugs?&lt;/h3&gt;

&lt;p&gt;A lot of bug fixes have been done. Let me explain &lt;a href=&quot;blog/version-0.5.0.html#some-interesting-bug-fixes&quot;&gt;some interesting ones&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;in-depth-details&quot;&gt;In-depth Details&lt;/h2&gt;

&lt;h3 id=&quot;purl-breaking-details&quot;&gt;PURL breaking details&lt;/h3&gt;

&lt;p&gt;At the beginning of this story, I only wanted to add a &lt;a href=&quot;issues/close/5dd3-plugin-scope.html&quot;&gt;scope&lt;/a&gt; for plugins. That way, if you install a plugin with &lt;code&gt;gitroot&lt;/code&gt; as the scope, you can think &amp;ldquo;it looks less dangerous&amp;rdquo; than &lt;code&gt;anyother&lt;/code&gt; scope. Just like I suspect less &lt;code&gt;@angular&lt;/code&gt; than some &lt;code&gt;odd&lt;/code&gt; Node.js package (well, the npm ecosystem is maybe a bad example for that&amp;hellip; but the only one right now).&lt;/p&gt;

&lt;p&gt;So, my simple idea was to transform &lt;code&gt;url: https://gitroot.dev/releases/0.4.0/ladybug-0.0.4.wasm&lt;/code&gt; into something like &lt;code&gt;url: https://gitroot.dev/releases/0.4.0/gitroot-ladybug-0.0.4.wasm&lt;/code&gt;, so that a &lt;code&gt;gitroot-*.wasm&lt;/code&gt; is affiliated with me. In the &lt;a href=&quot;plugins/index.html&quot;&gt;registry&lt;/a&gt;, I can show the scope.&lt;/p&gt;

&lt;p&gt;Another example is if someone forks &lt;code&gt;ladybug&lt;/code&gt; and wants to release it in the &lt;a href=&quot;plugins/index.html&quot;&gt;registry&lt;/a&gt;. Previously there was no option to do that, so adding a scope seemed to be the best choice.&lt;/p&gt;

&lt;p&gt;During my research on a way to model this scope, I found the &lt;a target=&quot;_blank&quot; href=&quot;https://ecma-international.org/publications-and-standards/standards/ecma-427/&quot;&gt;ECMA-427 Package-URL (PURL)&lt;/a&gt; specification. PURL stands for Package URL (I call them Plugin URL), a way to express many packages as a URL. What a perfect fit for the GitRoot plugin system!&lt;/p&gt;

&lt;p&gt;This is how a PURL is represented: &lt;code&gt;scheme:type/namespace/name@version?qualifiers#subpath&lt;/code&gt;. In GitRoot, I use only &lt;code&gt;pkg:gitroot/$scope/$name@$version?checksum=$checksum&amp;amp;registry=$urlToDownload&lt;/code&gt; (maybe a default registry like &lt;code&gt;https:%2F%2F%2Fgitroot.dev&lt;/code&gt; will be added in a next version, I&amp;rsquo;m not sure about that for now). There is no specification about what a registry should do to implement the GitRoot specification. For now, GitRoot will download &lt;code&gt;$urlToDownload/$scope-$name-$version.wasm&lt;/code&gt;, and that&amp;rsquo;s all.&lt;/p&gt;

&lt;p&gt;Thanks to PURL, I can delete a lot of other properties in the &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt; file, like version, checksum, and name. I also use it a lot in the code to refer to a plugin. Previously I only used the name, but now I use a full PURL. So, for example, if a plugin wants to call another plugin (thanks to the &lt;code&gt;CallFunc&lt;/code&gt; API), it can define it as &lt;code&gt;pkg:gitroot/$scope/$name&lt;/code&gt; (to mean all possible versions), or an exact version with &lt;code&gt;pkg:gitroot/$scope/$name@$version&lt;/code&gt;. Adding a checksum and even a registry is also possible.&lt;/p&gt;

&lt;p&gt;That was a big refactor to do, but I&amp;rsquo;m pretty happy with the result.&lt;/p&gt;

&lt;h3 id=&quot;gitroot-communication&quot;&gt;GitRoot communication&lt;/h3&gt;

&lt;p&gt;A lot has occured during this summer, on the communication side too.&lt;/p&gt;

&lt;p&gt;I want to thank &lt;a target=&quot;_blank&quot; href=&quot;https://adullact.org/&quot;&gt;Adullact&lt;/a&gt; and particularly Matthieu for letting me present the first show at the &lt;a target=&quot;_blank&quot; href=&quot;https://adullact.org/congres&quot;&gt;Adullact congress&lt;/a&gt;. The video is only in French without subtitles: &lt;a target=&quot;_blank&quot; href=&quot;https://video.adullact.org/w/kYxpgoVZfoPcLsDe19jvQw&quot;&gt;Souveraineté : Savez-vous vraiment qui contrôle vos logiciels ?&lt;/a&gt;. It was a great two-day congress, I talked to a lot of passionate people. And for once, they weren&amp;rsquo;t developers.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://gitroot.dev/branches/v0.5/blog/version-0.5.0-cover.png&quot; alt=&quot;Chart with number of unique visitor, a uge pick (from 500 to 13k) is visible around end of jul start of aout.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Then the first appearance on &lt;a target=&quot;_blank&quot; href=&quot;https://lobste.rs/s/z01edy&quot;&gt;lobste.rs&lt;/a&gt; and &lt;a target=&quot;_blank&quot; href=&quot;https://news.ycombinator.com&quot;&gt;Hacker News&lt;/a&gt; occurred. Thanks &lt;a target=&quot;_blank&quot; href=&quot;https://fosstodon.org/@hasnep&quot;&gt;hasnep&lt;/a&gt; for sharing, you can follow the initial exchange on my &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge/statuses/01KXTPKHP5KVWHETKSMC24B32E&quot;&gt;GoToSocial&lt;/a&gt;. As the image shows, there was a spike in the number of visitors, emails, and questions asked on the platforms and in private. That was a great moment to experience.&lt;/p&gt;

&lt;p&gt;After the mentions on these platforms, some other cool articles popped up (I won&amp;rsquo;t link them as they are principally AI-generated slop). But there was also a video showing how to run GitRoot in a &lt;a target=&quot;_blank&quot; href=&quot;https://nanos.org/&quot;&gt;nanoVM&lt;/a&gt;: &lt;a target=&quot;_blank&quot; href=&quot;https://www.youtube.com/watch?v=g5nW9g5T4wU&quot;&gt;https://www.youtube.com/watch?v=g5nW9g5T4wU&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another particular point of pride was the first mention by &lt;a target=&quot;_blank&quot; href=&quot;https://nesbitt.io/&quot;&gt;Andrew Nesbitt&lt;/a&gt; in the blog article &lt;a target=&quot;_blank&quot; href=&quot;https://nesbitt.io/2026/08/20/issues-in-the-repo.html&quot;&gt;Issues in the Repo&lt;/a&gt;. Thanks to Damien for talking about GitRoot to Andrew. Since you deleted the toot, I guess you don&amp;rsquo;t want to be cited here. If that&amp;rsquo;s not the case, contact me.&lt;/p&gt;

&lt;p&gt;And GitRoot has been added to &lt;a target=&quot;_blank&quot; href=&quot;https://alternativeto.net/software/gitroot/about/&quot;&gt;AlternativeTo&lt;/a&gt;. I don&amp;rsquo;t know if this will lead to anything, but hey, I&amp;rsquo;m right alongside GitHub and GitLab :P&lt;/p&gt;

&lt;h3 id=&quot;sdkgenerator-full-story&quot;&gt;SdkGenerator full story&lt;/h3&gt;

&lt;p&gt;This experiment comes from a bored developer who didn&amp;rsquo;t want to use LLMs, but also didn&amp;rsquo;t want to rewrite the same models/objects/structs (call that what you want) four times. When I want to add a new function to the plugins-sdk, I do it in one language, and then, when it works, I need to rewrite it in all the other languages (more on that in the &amp;ldquo;&lt;a href=&quot;blog/modifying-API-SDK-plugin.html&quot;&gt;Modifying the API in the SDK Plugin&lt;/a&gt;&amp;rdquo; blog article).&lt;/p&gt;

&lt;p&gt;In my day job, I love to build tools that generate code. I have written a tool that takes Kotlin as input and generates TypeScript output. Then I wrote a tool that generates complete Angular components from Kotlin enums (yeah, bored of writing a form select from an enum). And finally, it generates complex Angular services from Spring annotations (when you are very bored, a lot of things can be generated).&lt;/p&gt;

&lt;p&gt;So it&amp;rsquo;s naturally that I started to write a tool that generates TS, Rust, and Zig from Go. But the experience is not conclusive&amp;hellip; A lot of language specifics cannot easily be shared by a tool. I&amp;rsquo;m not giving up&amp;hellip; I just have more interesting priorities (yes, I&amp;rsquo;m less bored on GitRoot than at my work ^^). But if this is the kind of thing you like, yes, you, reading that, please reach out to me, and I&amp;rsquo;ll explain all that I did and how you can take it further.&lt;/p&gt;

&lt;p&gt;By the way, I discovered another kind of tool that does a similar job: &lt;a target=&quot;_blank&quot; href=&quot;https://component-model.bytecodealliance.org/design/wit.html&quot;&gt;WIT&lt;/a&gt;. It&amp;rsquo;s not an official spec from the W3C, but since it&amp;rsquo;s from the Bytecode Alliance, it&amp;rsquo;s big enough to use. I have quickly looked at it, and the engine I use for Wasm is not really compatible for now. But I&amp;rsquo;m keeping that in mind; it could be a replacement for &lt;code&gt;SdkGenerator&lt;/code&gt; if nobody (including me) wants to do the job.&lt;/p&gt;

&lt;h3 id=&quot;max-anonymous-branches&quot;&gt;Max anonymous branches&lt;/h3&gt;

&lt;p&gt;When a lot of people started to talk about GitRoot, one thing haunted my nights: what if someone builds a simple script that clones GitRoot, creates 1,000 branches, and pushes them? I would need to &lt;code&gt;git push origin :branch&lt;/code&gt; for every single one of them. You don&amp;rsquo;t need to register to contribute to GitRoot, and that&amp;rsquo;s one of its strengths, but also one of its weaknesses.&lt;/p&gt;

&lt;p&gt;How to make that impossible without blocking all the beauty of GitRoot? One day, a simple solution came to me (yes, I love imagining scenarios running against me). Why not limit the number of branches from anonymous users? It&amp;rsquo;s simple after all: I need to count the number of branches made by anonymous users, and if it exceeds a limit, I stop accepting them. However, people I have added to my repo because of a previous contribution can still push new branches. That is what I added, but&amp;hellip; there is always a but&amp;hellip; counting the number of branches is lightweight, but counting the number of branches from an anonymous user is really expensive. Life is full of choices, and I made one.&lt;/p&gt;

&lt;p&gt;When the branch limit is reached, we stop accepting anonymous contributions. That means if you set 10 as the maximum and you make 20 branches, nobody else can contribute. Yes, it&amp;rsquo;s not perfect, but it&amp;rsquo;s way better than having nightmares every night.&lt;/p&gt;

&lt;p&gt;If an admin configures the forge with &lt;code&gt;NbMaxBranchesPerProjectBeforeRejectingAnonymous=10&lt;/code&gt;, that means when a project has 10 branches (who added them doesn&amp;rsquo;t matter), the next anonymous user (not present in &lt;code&gt;.gitroot/users.yml&lt;/code&gt;) will be rejected. Of course, all known users can continue to create as many branches as they want.&lt;/p&gt;

&lt;p&gt;A project can set a lower number, &lt;code&gt;NbMaxBranchesBeforeRejectingAnonymous=5&lt;/code&gt;, but if the number is bigger, it will have no effect (e.g., if set to &lt;code&gt;NbMaxBranchesBeforeRejectingAnonymous=15&lt;/code&gt;, GitRoot will use 10 as set by the forge administrator).&lt;/p&gt;

&lt;p&gt;A neat trick: setting &lt;code&gt;NbMaxBranchesBeforeRejectingAnonymous=0&lt;/code&gt; blocks all anonymous pushes. Alternatively, you can use the &lt;a href=&quot;doc/how-tos/no_anonymous_user.html&quot;&gt;Not accepting anonymous contribution&lt;/a&gt; guide. The main difference is the error message: the first gives &lt;code&gt;anonymous branches not allowed&lt;/code&gt;, while the latter shows &lt;code&gt;you can&#39;t write in $branchName&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;zig-plugin-sdk&quot;&gt;Zig plugin-sdk&lt;/h3&gt;

&lt;p&gt;With every release, I try to add a new plugin-sdk language. This time I picked &lt;a target=&quot;_blank&quot; href=&quot;https://ziglang.org/&quot;&gt;zig&lt;/a&gt; because it is a language that natively manages Wasm and WASI, so I was pretty sure I would finish this implementation.&lt;/p&gt;

&lt;p&gt;I loved the error system, compared to Rust (optionals) and Go (multiple returns), Zig chose an in-between solution with &lt;code&gt;!&lt;/code&gt; and some language keywords. But what totally blew my mind is the &lt;code&gt;comptime&lt;/code&gt; feature, where the compiler can become an interpreter for compilation output. Amazing idea, I am pretty sure a lot of things will follow this path.&lt;/p&gt;

&lt;p&gt;Long story short, I spent a long time discovering the language, and it was a very good time for me (it changes your perspective, seeing the same problem solved another way)&amp;hellip; but nothing related directly to GitRoot.&lt;/p&gt;

&lt;p&gt;As with all the plugin-sdks, there is unfortunately no way for you to use it yet. I don&amp;rsquo;t release them in registries until the API is stable, and a lot of breaking changes are coming in the next release.&lt;/p&gt;

&lt;h3 id=&quot;pluginsdk-improvements&quot;&gt;PluginSdk improvements&lt;/h3&gt;

&lt;p&gt;Regarding the plugin-sdk, here are a lot of new capabilities for plugins to use:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;schedule&lt;/code&gt;: you can allow a plugin to be called at a scheduled time. The format is a cron expression with 5 segments as &lt;code&gt;&amp;lt;minute&amp;gt; &amp;lt;hour&amp;gt; &amp;lt;day&amp;gt; &amp;lt;month&amp;gt; &amp;lt;weekday&amp;gt;&lt;/code&gt;. Example:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;purl&lt;/span&gt;: pkg:gitroot/gitroot/firefly@0.0.1?checksum=sha256:fae956f5cc9239a87b1c2a149a5466fec85476377de4ada33dda190edb3700c4&amp;amp;registry=https:%2F%2Fgitroot.dev%2Freleases%2F0.5.0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;active&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;schedule&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;* * * * *&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That means the firefly plugin will be executed on the main branch every minute. Refer to the &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/adhocore/gronx#cron-expression&quot;&gt;gronx library documentation&lt;/a&gt; for cron expression details.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;httpClient&lt;/code&gt;: plugins can make HTTP requests. I have added standard verbs (&lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt; and &lt;code&gt;DELETE&lt;/code&gt;), if you need more, please let me know. You can also add headers as a &lt;code&gt;map[string][]string&lt;/code&gt;. In the next version, I will add permission a plugin must request beforehand.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;callFunc&lt;/code&gt; configuration: you can add a configuration for the called plugin within the caller plugin&amp;rsquo;s &lt;code&gt;callFunc&lt;/code&gt;. This makes it possible to tell the called plugin what its configuration is in this context. Previously, an empty config was set, so the plugin&amp;rsquo;s defaults (if any) were used. Example:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;callFunc&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;pluginPURL&lt;/span&gt;: pkg:gitroot/gitroot/apex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;funcName&lt;/span&gt;: renderHtml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;branchesDir&lt;/span&gt;: branches
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;favicon&lt;/span&gt;: logo.png
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;footer&lt;/span&gt;: &amp;lt;p&amp;gt;Start with &amp;lt;code&amp;gt;git clone ssh://gitroot.dev/&amp;lt;/code&amp;gt; 🚀&amp;lt;/p&amp;gt;&amp;lt;small&amp;gt;Hosted with ❤️ by Gitroot&amp;lt;/small&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;generateGitWorktree&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt;9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;header&lt;/span&gt;: ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;init&lt;/code&gt; function, one to rule them all: I have refactored the &lt;code&gt;init()&lt;/code&gt; function of the plugin-sdk to provide an &lt;code&gt;initKind model.InitKind&lt;/code&gt;, which can be &lt;code&gt;confhaschange&lt;/code&gt;, &lt;code&gt;diff&lt;/code&gt;, &lt;code&gt;schedule&lt;/code&gt;, or &lt;code&gt;funccall&lt;/code&gt;. This lets the plugin know in which context it is running, and this architecture allows future hooks to be added without breaking the API.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pull-iterator&lt;/code&gt;: I have introduced pull-iterators to be able to group reports without sending all reports to the Wasm memory. In a future version, I will use this mechanism to transfer commits and files directly to Wasm.&lt;/p&gt;

&lt;h3 id=&quot;sftp-feature&quot;&gt;Sftp feature&lt;/h3&gt;

&lt;p&gt;Users can access every repository&amp;rsquo;s web directory through SFTP. To do this, you can use the same SSH key that you use for your Git repository. When you log in with &lt;code&gt;sftp -P $SshPort $SshAddress:/$repo/&lt;/code&gt; (like &lt;code&gt;sftp -P 4545 127.0.0.1:/repo1/&lt;/code&gt;), you will see the list of web representations for your repository. Anyone can do this, but only those in the &amp;ldquo;owner&amp;rdquo; section of &lt;code&gt;.gitroot/users.yml&lt;/code&gt; have write access.&lt;/p&gt;
&lt;blockquote style=&quot;border-color: #006ac7&quot;&gt;

&lt;p&gt;Be careful: this feature is in alpha, so don&amp;rsquo;t write scripts around it for now. I will probably change the directory hierarchy &lt;a href=&quot;issues/d42a-sftp-better-tree.html&quot;&gt;#d42a&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To enable this feature, you need to change &lt;code&gt;activesftp: false&lt;/code&gt; to &lt;code&gt;activesftp: true&lt;/code&gt; in &lt;code&gt;.gitroot/forgeConfig.yml&lt;/code&gt; at the root repository of your instance. Don&amp;rsquo;t forget to restart GitRoot after that, otherwise the parameter will not be taken into account by GitRoot.&lt;/p&gt;

&lt;h3 id=&quot;some-interesting-bug-fixes&quot;&gt;Some interesting bug fixes&lt;/h3&gt;

&lt;p&gt;Of course, a lot of bug fixes have been included in this release. I will present only the most interesting ones, and you can find a more complete list in the &lt;a href=&quot;CHANGELOG.html&quot;&gt;changelog&lt;/a&gt; and an even more thorough one in the commits.&lt;/p&gt;

&lt;p&gt;The first annoying bug occurred when &lt;a href=&quot;plugins/name/grafter.html&quot;&gt;Grafter&lt;/a&gt; committed to a file, but &lt;a href=&quot;plugins/name/apex.html&quot;&gt;Apex&lt;/a&gt; did not render that file if the Grafter plugin was placed after Apex in the configuration. In fact, even if you put Grafter before Apex, the bug persisted because Apex &lt;code&gt;reports&lt;/code&gt; the rendering back to Grafter, meaning it missed that final report. Now, I have added a subsequent loop where plugins can only write to the web directory (and cache) but not to Git. This way, all plugins loop over commits and files to generate new commits, and only afterwards do we loop through all plugins to see if they have anything to write to the web (or cache) excluding Git to avoid an infinite loop.&lt;/p&gt;

&lt;p&gt;Another annoying issue happened when you changed a plugin&amp;rsquo;s configuration, causing it to enter a worktree loop (looping over files in the worktree instead of commits). Consequently, the commits generated by this loop were not propagated to other plugins, causing widespread rendering misses on the web.&lt;/p&gt;

&lt;p&gt;Finally, if you decided to add a plugin multiple times in your &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt; file (to have multiple configurations, for instance), only the first instance was actually loaded and executed. Now, you can include a plugin as many times as you want, with any configuration you choose, and they will all be executed.&lt;/p&gt;

&lt;h2 id=&quot;what-next&quot;&gt;What next?&lt;/h2&gt;

&lt;p&gt;New plugins, bug fixes, updates&amp;hellip; Yes, until now this article was only about GitRoot, but what is GitRoot without its plugins? The immediate next thing I will do is write an article about what new plugin versions can do.&lt;/p&gt;

&lt;p&gt;Then I will probably start working on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discover GitRoot without installing anything.&lt;/li&gt;
&lt;li&gt;Create your own plugins with ease.&lt;/li&gt;
&lt;li&gt;Transform GitRoot into a registry for (at least) Go, Rust, NPM and Zig.&lt;/li&gt;
&lt;li&gt;Federation with GitRoot but also other forges.&lt;/li&gt;
&lt;li&gt;Implement something for users (how to represent them).&lt;/li&gt;
&lt;li&gt;Manage more Git functions in plugins (branch, pull, push, rebase).&lt;/li&gt;
&lt;li&gt;Use pull-iterators to stabilise the plugin-sdk.&lt;/li&gt;
&lt;li&gt;Easier configuration of plugins.&lt;/li&gt;
&lt;li&gt;Auto-update of plugins.&lt;/li&gt;
&lt;li&gt;New language plugin-sdk.&lt;/li&gt;
&lt;li&gt;Air-gapped version of GitRoot.&lt;/li&gt;
&lt;li&gt;Make every repository a gemini capsule.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you compare this with the &lt;a href=&quot;blog/version-0.4.0.html#what-next&quot;&gt;last v0.4 what next&lt;/a&gt; section, there are a lot more things. Every time I finish a feature (or don&amp;rsquo;t, see the &lt;a href=&quot;blog/version-0.5.0.html#sdkgenerator-full-story&quot;&gt;plugin-sdk generator&lt;/a&gt;), I can add ten more new features to do. That&amp;rsquo;s why I love GitRoot, and that&amp;rsquo;s why &lt;code&gt;I Move slowly, and take care of things&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Special thanks to &lt;code&gt;Ryuno-Ki&lt;/code&gt;, &lt;code&gt;Matthieu&lt;/code&gt; and &lt;code&gt;ks&lt;/code&gt; from the &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/#gitroot:matrix.org&quot;&gt;#gitroot:matrix.org&lt;/a&gt; Matrix room for reviewing and correcting this article. Want to help with future posts? Join us in the room!&lt;/em&gt;&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.5.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.4.0.md</link><title>0.4.0</title><pubDate>2026/04/22</pubDate><description>&lt;h1 id=&quot;the-teenager-starts-building-its-own-future&quot;&gt;The teenager starts building its own future&lt;/h1&gt;

&lt;h2 id=&quot;if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes&lt;/h2&gt;

&lt;p&gt;A lot has been done in this new release of GitRoot. When I saw other FOSS projects claiming &amp;ldquo;this is the biggest release&amp;rdquo;, I thought it was a joke. But in fact, it&amp;rsquo;s true: every new release gets bigger and bigger. It&amp;rsquo;s just astonishing to start a sprint with almost zero ideas and, 4 months later, see all of this accomplished.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you skim this article, don&amp;rsquo;t forget to check out the conclusion about &lt;a href=&quot;blog/version-0.4.0.html#what-next&quot;&gt;&amp;ldquo;what&amp;rsquo;s next?&amp;rdquo;&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;the-thing-i-m-most-proud-of&quot;&gt;The thing i&amp;rsquo;m most proud of&lt;/h3&gt;

&lt;p&gt;Executors can finally build GitRoot! As I love to say: GitRoot can build GitRoot on GitRoot. And it was a pain to do. &lt;a href=&quot;blog/version-0.4.0.html#executor&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-thing-with-the-most-visibility&quot;&gt;The thing with the most visibility&lt;/h3&gt;

&lt;p&gt;The plugin registry is here! After a lot of reading and reflecting, I started this &amp;ldquo;epic story&amp;rdquo;. Today is only the beginning, but you can now find plugins directly at &lt;a href=&quot;plugins/index.html&quot;&gt;gitroot.dev/plugins&lt;/a&gt;. What&amp;rsquo;s next? &lt;a href=&quot;blog/version-0.4.0.html#plugin-registry&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-thing-that-changes-everything-but-was-so-simple&quot;&gt;The thing that changes everything but was so simple&lt;/h3&gt;

&lt;p&gt;Previously, a branch (and graft) couldn&amp;rsquo;t be rendered on the web. The problem was: if I have an &lt;code&gt;index.md&lt;/code&gt; in my git that renders at &lt;code&gt;myinstance.com/index.html&lt;/code&gt;, anyone can create a branch. If a &amp;ldquo;bad user&amp;rdquo; comes along, changes everything in this file, creates a branch, and pushes for review, where should this file render? Certainly not at &lt;code&gt;myinstance.com/index.html&lt;/code&gt;. &lt;a href=&quot;blog/version-0.4.0.html#apex-render-branch&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-thing-that-was-more-complex-than-i-thought&quot;&gt;The thing that was more complex than I thought&lt;/h3&gt;

&lt;p&gt;Apex was a big plugin. Basically, it manages the rendering of your git files to the web. But over time, it started to grow out of control because of markdown, code, worktrees, emojis&amp;hellip; A simple (in theory) solution was to split it into multiple plugins. &lt;a href=&quot;blog/version-0.4.0.html#plugin-deps&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-things-i-m-just-happy-are-done&quot;&gt;The things I&amp;rsquo;m just happy are done&lt;/h3&gt;

&lt;p&gt;No more mandatory signed commits! Well, it&amp;rsquo;s still better if you sign them, but this change means the &lt;code&gt;.gitroot/init.sh&lt;/code&gt; has been &lt;del&gt;deleted&lt;/del&gt; moved to a plugin. &lt;a href=&quot;blog/version-0.4.0.html#sigma-plugin&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Moving a file from git to web was hard, deleting a file was &amp;ldquo;not implemented&amp;rdquo;, and a lot of APIs were missing in the plugin-sdk. I got my hands dirty with all that, and now everything is running smoothly. &lt;a href=&quot;blog/version-0.4.0.html#fs-plugin-sdk&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;in-depth-details&quot;&gt;In-depth Details&lt;/h2&gt;

&lt;h3 id=&quot;executor&quot;&gt;Executor&lt;/h3&gt;

&lt;p&gt;When I started this feature, I just wanted a lightweight container to run &lt;code&gt;make build&lt;/code&gt; and output the result as a &lt;code&gt;gitroot-next&lt;/code&gt; canary binary. If I had known how much time and energy it would actually cost, I’m not sure I would have taken this road.&lt;/p&gt;

&lt;p&gt;At first, I chose &lt;a href=&quot;doc/technicals/executors.html#bwrap-executor&quot;&gt;bubblewrap&lt;/a&gt; as a lightweight container, and immediately started building a &lt;a href=&quot;doc/technicals/executors.html#container-executor&quot;&gt;container&lt;/a&gt; version. Then I realized I needed a &lt;a href=&quot;doc/technicals/executors.html#baremetal-executor&quot;&gt;bareMetal&lt;/a&gt; one. And then, I needed a way to run it on other machines, so &lt;a href=&quot;doc/technicals/executors.html#ssh-executor&quot;&gt;ssh&lt;/a&gt; came along.&lt;/p&gt;

&lt;p&gt;But when I launched it for the first time, I discovered that having &lt;code&gt;make&lt;/code&gt; isn&amp;rsquo;t enough for GitRoot, I need Golang, Rust, TinyGo, and so on. &amp;ldquo;No problem,&amp;rdquo; I thought, &amp;ldquo;I&amp;rsquo;ll use &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt; to manage that.&amp;rdquo; But running &lt;code&gt;mise install&lt;/code&gt; takes a lot of time (like 10-15 minutes on my computer). So, I built a cache system to ensure that hit only happens the first time.&lt;/p&gt;

&lt;p&gt;Still not the end! After that, I needed to configure port numbers, so I built a complete environment variable system, and used it to configure some &lt;code&gt;mise&lt;/code&gt; paths.&lt;/p&gt;

&lt;p&gt;Is it finished? Not yet! To be able to move a binary generated from a bwrap environment to the web, it has to travel a lot. And by &amp;ldquo;a lot,&amp;rdquo; I mean moving it to a place where the plugin that launched the executor can access it: its own cache. Then the plugin needs to move it from there to the web. No problem, I just added a bunch of &lt;a href=&quot;blog/modifying-API-SDK-plugin.html&quot;&gt;SDK API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After all that, some commands finally started working&amp;hellip; well, kind of. When I ran a bwrap or a container command, I couldn&amp;rsquo;t get the stats of what was happening inside. Having process stats is something I’m proud of (even if it’s still evolving in future versions), but seeing that bwrap reported 20 MB of RAM while the sub-process was actually consuming 80 MB was very frustrating. Furthermore, I needed a way to extract stats from the remote SSH executor.&lt;/p&gt;

&lt;p&gt;I tried a lot of things, but ultimately, I chose to build an &lt;a href=&quot;app/executor/main.go&quot;&gt;executor CLI&lt;/a&gt; to handle it. I now inject that binary inside your bwrap, container, or SSH environment. This little CLI launches commands, gathers system stats, command status, and logs. It packs everything into a well-formatted JSON and returns it to the executor in GitRoot. GitRoot then manages artifacts/caches and hands the result back to the calling plugin (probably &lt;a href=&quot;plugins/name/hop.html&quot;&gt;hop&lt;/a&gt;), which tells you what’s happening. In this case, it reports back so the &lt;a href=&quot;plugins/name/grafter.html&quot;&gt;grafter plugin&lt;/a&gt; can include the info in the current graft.&lt;/p&gt;
&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;295.9538&quot; height=&quot;429.0792&quot; viewBox=&quot;0 0 295.9538 429.0792&quot;&gt;&lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;295.9538&quot; height=&quot;429.0792&quot; fill=&quot;transparent&quot;/&gt;&lt;defs&gt;&lt;marker id=&quot;arrow-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 10 5 L 0 10 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-start-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;4.5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 5 L 10 10 L 10 0 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;path id=&quot;edge-0&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-0&quot; d=&quot;M 210.589,59.000 L 210.589,107.236&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot; x=&quot;105.97&quot; y=&quot;62.00&quot; width=&quot;90.62&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;151.28&quot; y=&quot;79.70&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;151.28&quot; dy=&quot;0.00&quot;&gt;Exec cmds&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-1&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-1&quot; d=&quot;M 210.679,158.236 L 210.679,206.472&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;path id=&quot;edge-2&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-2&quot; d=&quot;M 210.835,257.472 L 210.835,305.708&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-2&quot; data-label-kind=&quot;center&quot; x=&quot;17.60&quot; y=&quot;260.47&quot; width=&quot;179.24&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-2&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;107.22&quot; y=&quot;278.17&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;107.22&quot; dy=&quot;0.00&quot;&gt;bwrap/container/ssh&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-3&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-3&quot; d=&quot;M 210.955,356.708 L 210.955,370.079&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;path id=&quot;edge-4&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-4&quot; d=&quot;M 166.834,395.579 L 153.002,395.579 L 120.042,347.208 L 133.874,347.208&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;path id=&quot;edge-5&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-5&quot; d=&quot;M 133.874,315.208 L 8.000,315.208 L 8.000,247.972 L 165.077,247.972&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-5&quot; data-label-kind=&quot;center&quot; x=&quot;46.42&quot; y=&quot;258.31&quot; width=&quot;206.12&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-5&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;149.48&quot; y=&quot;276.01&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;149.48&quot; dy=&quot;0.00&quot;&gt;status/code/logs/stats&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-6&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-6&quot; d=&quot;M 165.077,215.972 L 41.355,215.972 L 41.355,148.736 L 153.161,148.736&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-6&quot; data-label-kind=&quot;center&quot; x=&quot;56.06&quot; y=&quot;159.07&quot; width=&quot;206.12&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-6&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;159.12&quot; y=&quot;176.77&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;159.12&quot; dy=&quot;0.00&quot;&gt;status/code/logs/stats&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-7&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-7&quot; d=&quot;M 153.161,116.736 L 26.706,116.736 L 26.706,33.500 L 167.201,33.500&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-7&quot; data-label-kind=&quot;center&quot; x=&quot;57.12&quot; y=&quot;59.83&quot; width=&quot;206.12&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-7&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;160.18&quot; y=&quot;77.53&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;160.18&quot; dy=&quot;0.00&quot;&gt;status/code/logs/stats&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;rect x=&quot;167.20&quot; y=&quot;8.00&quot; width=&quot;86.75&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.58&quot; y=&quot;37.00&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.58&quot; dy=&quot;0.00&quot;&gt;Hop&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;153.16&quot; y=&quot;107.24&quot; width=&quot;114.88&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.60&quot; y=&quot;136.24&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.60&quot; dy=&quot;0.00&quot;&gt;GitRoot&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;133.87&quot; y=&quot;305.71&quot; width=&quot;154.08&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.91&quot; y=&quot;334.71&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.91&quot; dy=&quot;0.00&quot;&gt;executor cli&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;166.83&quot; y=&quot;370.08&quot; width=&quot;88.32&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;211.00&quot; y=&quot;399.08&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;211.00&quot; dy=&quot;0.00&quot;&gt;cmd&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;165.08&quot; y=&quot;206.47&quot; width=&quot;91.36&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.76&quot; y=&quot;235.47&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.76&quot; dy=&quot;0.00&quot;&gt;exec&lt;/tspan&gt;&lt;/text&gt;&lt;g transform=&quot;translate(210.59 107.24) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(210.68 206.47) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(210.84 305.71) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(210.95 370.08) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(133.87 347.21) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(165.08 247.97) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(153.16 148.74) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(167.20 33.50) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;/svg&gt;
&lt;p&gt;Phew, is that enough now? It should be, but we&amp;rsquo;ll see how it performs in real-time on &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev&quot;&gt;gitroot.dev&lt;/a&gt;. Not everything is finished: for example, the SSH executor doesn’t manage cache yet &lt;a href=&quot;issues/94af-executor-cache.html&quot;&gt;#94af&lt;/a&gt; and can&amp;rsquo;t use the container strategy &lt;a href=&quot;issues/f050-ssh-executor-subCmd.html&quot;&gt;#f050&lt;/a&gt;. You can’t run the executor on other architectures &lt;a href=&quot;issues/4d49-executor-compile-other-archs.html&quot;&gt;#4d49&lt;/a&gt; yet, and I’m sure you’ll soon want a way to clean up temp directories &lt;a href=&quot;issues/647f-clean-executor-temp-dir.html&quot;&gt;#647f&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But hey, let’s celebrate what we’ve built before jumping into the next sprint!&lt;/p&gt;

&lt;h3 id=&quot;plugin-registry&quot;&gt;Plugin registry&lt;/h3&gt;

&lt;p&gt;Until now, explaining GitRoot was always about &amp;ldquo;data in git and the plugin system&amp;rdquo;, but what exactly are the plugins? Where are they? What can they do? All of that was a bit mysterious. With this milestone, I’m answering some of those questions with a first shot at a registry. Be careful, though: this first version is just a &amp;ldquo;hand-written&amp;rdquo; preview of what it could be in the future. If you look at the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/plugins/index.md&quot;&gt;source&lt;/a&gt;, you’ll see that everything is ready to be automated, but nothing is&amp;hellip; yet.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;m not entirely sure what you, as a consumer, want to see here. Probably an explanation of what the plugin does, some docs, and the available versions. But as a plugin creator, you likely want to manage all of that in your own repository instead of the &lt;a href=&quot;plugins/index.html&quot;&gt;gitroot.dev registry&lt;/a&gt;. I chose to keep the registry very light and put the details directly in the &lt;a href=&quot;app/plugins/apex/index.html&quot;&gt;plugin&amp;rsquo;s directory&lt;/a&gt; to give you an idea of what your own repo could look like.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ll need to iterate on this. Even if I choose to embed package details in the registry, I still need to decide how: maybe via a JSON in your repo, maybe in the registry itself, or perhaps inside your package (WASM)? I don&amp;rsquo;t know yet what’s simplest for you and for the registry.&lt;/p&gt;

&lt;p&gt;Anyway, I hope this registry helps you understand what a plugin is and how it shapes your forge. Some are small (&lt;a href=&quot;plugins/name/ladybug.html&quot;&gt;ladybug&lt;/a&gt;), others are massive (&lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex&lt;/a&gt;), and some aren&amp;rsquo;t usable solo as they are &lt;a href=&quot;blog/version-0.4.0.html#plugin-deps&quot;&gt;dependencies&lt;/a&gt; for others (like &lt;a href=&quot;plugins/name/apex_mermaid.html&quot;&gt;apx_mermaid&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Since plugins are now decoupled from GitRoot, I’ve moved all changelogs from the &lt;a href=&quot;CHANGELOG.html&quot;&gt;general&lt;/a&gt; one to the individual plugin directories. This way, I’ll be able to release plugin versions independently of GitRoot (and vice versa). I hope to break the plugin API less often, even if I know I probably will in the near future. All of this paves the way for you to build your own plugins and I hope to see them in the gitroot.dev registry soon. Or, of course, you can just build your own registry!&lt;/p&gt;

&lt;h3 id=&quot;apex-render-branch&quot;&gt;Apex render branch&lt;/h3&gt;

&lt;p&gt;For as long as I can remember, this has been a problem with GitRoot: how to render arbitrary changes, especially deletions, on the web? I had imagined a lot of strategies. The last one was to render inside a directory named after the git tree ID (the abstract representation of a working directory). Then, I’d create a symlink between that tree and a main directory.&lt;/p&gt;

&lt;p&gt;The idea was: if you push a file in a branch, git gives you a tree ID like &lt;code&gt;a654fs&lt;/code&gt;. It would be represented as &lt;code&gt;mydomain.com/myBranch/file -&amp;gt; mydomain.com/tree/a654fs/file&lt;/code&gt;. When you merged that branch, GitRoot would simply symlink the main directory to it: &lt;code&gt;mydomain.com/file -&amp;gt; mydomain.com/tree/a654fs/file&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Elegant, but honestly, a total over-engineered mess. When I actually sat down to code this, I ended up adding just two lines to the &lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex plugin&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; p.currentCommit.Branch &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; p.currentCommit.Branch &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; p.config.defaultBranch {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	path = filepath.&lt;span style=&quot;color:#50fa7b&quot;&gt;Join&lt;/span&gt;(p.config.branchesDir, p.currentCommit.Branch, path)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And&amp;hellip; yeah, that was enough. I&amp;rsquo;ve been overthinking this since the very beginning of GitRoot! Rendering files from branches into a dedicated branches directory&amp;hellip; It&amp;rsquo;s simple. Just like GitRoot should be.&lt;/p&gt;

&lt;p&gt;So, what does it mean in practice? The file is now rendered at &lt;code&gt;myinstance.com/mybranch/index.html&lt;/code&gt; and the graft at &lt;code&gt;myinstance.com/mybranch/grafts/mybranch.html&lt;/code&gt;. How did I not think of something this simple before?&lt;/p&gt;

&lt;h3 id=&quot;plugin-deps&quot;&gt;Plugin deps&lt;/h3&gt;

&lt;p&gt;The catalyst for this development was the &lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex plugin&lt;/a&gt;. At over 3 MB, it was by far the largest plugin. It was packed with configuration, specifically a bunch of booleans to &lt;a href=&quot;issues/8b29-apex-working-tree.html&quot;&gt;toggle features&lt;/a&gt;. Furthermore, I needed to render Markdown in other plugins, like &lt;a href=&quot;plugins/name/pollen.html&quot;&gt;pollen&lt;/a&gt;, to create a proper &lt;a href=&quot;issues/83d8-pollen-content-flux.html&quot;&gt;RSS feed&lt;/a&gt;. It became clear to me: this plugin needed to be split into smaller, more specialized ones.&lt;/p&gt;

&lt;p&gt;So, I started building the &lt;a href=&quot;plugins/name/apex_markdown.html&quot;&gt;apex_markdown plugin&lt;/a&gt;, then &lt;a href=&quot;plugins/name/apex_code.html&quot;&gt;apex_code&lt;/a&gt;, and next week &lt;a href=&quot;plugins/name/apex_mermaid.html&quot;&gt;apex_mermaid&lt;/a&gt;. But what I hadn&amp;rsquo;t fully anticipated was the sheer amount of work required to make it all work together.&lt;/p&gt;

&lt;p&gt;What if a dependency is missing? What if a method is deprecated or removed in a newer version? To handle this, I’ve chosen to make all dependencies entirely optional for now. It’s an easy choice to make today, but I might change my mind tomorrow, after all, there’s a reason most package managers handle version ranges and resolution.&lt;/p&gt;

&lt;p&gt;This new architecture allows for adding tons of features without rebuilding an entire plugin. Imagine adding Org-mode or RST support: you just swap out &lt;a href=&quot;plugins/name/apex_markdown.html&quot;&gt;apex_mardown&lt;/a&gt; instead of touching the whole stack. It also allows other plugins to reuse existing behaviors effortlessly.&lt;/p&gt;

&lt;p&gt;With this in place, I’m not even sure if I’ll keep the &lt;code&gt;report&lt;/code&gt; system, which was an other way of passing information between plugins.&lt;/p&gt;

&lt;h3 id=&quot;sigma-plugin&quot;&gt;Sigma plugin&lt;/h3&gt;

&lt;p&gt;Signed commits should be the default in git today. With SSH signing, it’s easy to do, and everyone should use it because impersonating a name and email is way too easy in git. When I started GitRoot, I made signed commits mandatory before allowing a push. But in fact, making signatures mandatory should be a choice for the instance owner. GitRoot is all about the freedom to choose how your forge works for you.&lt;/p&gt;

&lt;p&gt;Furthermore, a friend told me he preferred signing with PGP and already had his setup ready. So why force him to use a &lt;code&gt;.gitroot/init.sh&lt;/code&gt;? It’s frustrating when you want to test a new forge and you’re forced to run random sh commands, even if it&amp;rsquo;s just to configure your git client.&lt;/p&gt;

&lt;p&gt;With this release, I have completely removed &lt;code&gt;init.sh&lt;/code&gt; from GitRoot itself. But of course, as always, I’ve created a plugin to &amp;ldquo;almost&amp;rdquo; recreate this feature. The only difference is: instead of refusing your commit when you push, the &lt;a href=&quot;plugins/name/stigma.html&quot;&gt;stigma plugin&lt;/a&gt; will simply report unsigned or badly signed commits in your graft.&lt;/p&gt;

&lt;p&gt;Now, you have the choice to accept unsigned commits in your repo. You can even ignore the whole signing topic entirely if you don&amp;rsquo;t install the stigma plugin.&lt;/p&gt;

&lt;p&gt;That said, I’m still convinced that signing commits should remain the default.&lt;/p&gt;

&lt;h3 id=&quot;fs-plugin-sdk&quot;&gt;FS plugin-sdk&lt;/h3&gt;

&lt;p&gt;After finishing all the &lt;a href=&quot;blog/version-0.4.0.html#executor&quot;&gt;executors features&lt;/a&gt;, one thing was still missing: the ability for a plugin to move a file from one path to another. As explained in this &lt;a href=&quot;blog/modifying-API-SDK-plugin.html&quot;&gt;blog post&lt;/a&gt;, even though a plugin sees one single filesystem, GitRoot actually uses three different ones: one for cache (a directory), one for the web (a subdirectory), and a virtual one for the git working directory.&lt;/p&gt;

&lt;p&gt;I was missing several methods in both the Rust and TS SDKs, and the existing API was poorly designed with inconsistent naming. I decided to redesign everything and added a lot of new methods accessible via &lt;code&gt;FS&lt;/code&gt; in the SDK. Now, you can copy, move, delete, replace, and write files in every filesystem. Moving and copying can now be done between different filesystems, making it easy to make a file accessible on the web even if it originated from git or the cache.&lt;/p&gt;

&lt;p&gt;I have marked all old methods as deprecated, and they will be removed in the next version of GitRoot (or more specifically, the plugin-sdk, though they share the same release number for now).&lt;/p&gt;

&lt;h2 id=&quot;what-next&quot;&gt;What next?&lt;/h2&gt;

&lt;p&gt;I have a massive roadmap for GitRoot (in no particular order):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discover GitRoot without installing anything.&lt;/li&gt;
&lt;li&gt;Create your own plugins with ease.&lt;/li&gt;
&lt;li&gt;Transform GitRoot into a registry for (at least) Go, Rust, and NPM.&lt;/li&gt;
&lt;li&gt;Plugins should be usable via PURL.&lt;/li&gt;
&lt;li&gt;Plugins can send/receive HTTP requests.&lt;/li&gt;
&lt;li&gt;Plugins can be scheduled.&lt;/li&gt;
&lt;li&gt;Rebase go-git to take advantage of their performance improvements.&lt;/li&gt;
&lt;li&gt;Generate the plugin SDK automatically.&lt;/li&gt;
&lt;li&gt;Add plugin SDKs for (at least) Zig, Kotlin, and Python.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yeah, I could go on all night. And those are just core GitRoot features. I haven&amp;rsquo;t even mentioned making instances to build projects in hosted environments. I haven&amp;rsquo;t talked about specific plugins (sending a &amp;ldquo;toot&amp;rdquo; on commit, managing secrets with age, translating your app&amp;hellip;). I haven&amp;rsquo;t even touched on federating GitRoot with other GitRoot instances, or with Forgejo, SourceHut, or Radicle.&lt;/p&gt;

&lt;p&gt;All of that is possible, and I’m confident it will be accessible in the future. But &amp;ldquo;when&amp;rdquo;? That’s the real question. If you think you can help in any way (coding, of course, but also management, communication, or just to brainstorm), please &lt;a href=&quot;contact.html&quot;&gt;let me know&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is only the beginning. GitRoot will get better and better with every release.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.4.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.3.0.md</link><title>0.3.0</title><pubDate>2025/12/20</pubDate><description>&lt;h1 id=&quot;from-baby-to-teenager-gitroot-become-a-forge&quot;&gt;From &amp;ldquo;baby to teenager&amp;rdquo; GitRoot become a forge&lt;/h1&gt;

&lt;h2 id=&quot;if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes&lt;/h2&gt;

&lt;p&gt;Previously, GitRoot served as a &lt;strong&gt;Source Code Management (SCM)&lt;/strong&gt; system, allowing you to create Git repositories and manage access permissions. However, GitRoot was never intended to be &lt;em&gt;just&lt;/em&gt; an SCM. With this &lt;strong&gt;v0.3.0&lt;/strong&gt; release, it evolves into a full-fledged &lt;strong&gt;forge&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To activate this new capability on your instance, &lt;a href=&quot;blog/version-0.3.0.html#for-admin-activating-an-executor&quot;&gt;follow the administrator section&lt;/a&gt;. To start using it as a user, &lt;a href=&quot;blog/version-0.3.0.html#for-user-using-the-hop-plugin&quot;&gt;see the user section&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The introduction of &lt;strong&gt;executors&lt;/strong&gt; brings a crucial new feature: the &lt;a href=&quot;blog/version-0.3.0.html#plugin-api-report-and-reporter&quot;&gt;report and reporter plugin API&lt;/a&gt;. This API allows any plugin to provide informational feedback to the user. &lt;strong&gt;Important:&lt;/strong&gt; avoid reporting low-level technical details here (use the &lt;code&gt;log&lt;/code&gt; and &lt;code&gt;logError&lt;/code&gt; APIs for that). The initial implementation uses this feature to display command statistics (CPU, memory, etc.) from the new &lt;code&gt;hop&lt;/code&gt; plugin in a clean table via the &lt;code&gt;grafter&lt;/code&gt; plugin.&lt;/p&gt;

&lt;p&gt;With the new &lt;code&gt;hop&lt;/code&gt; plugin, GitRoot now supports 3 languages (Go, TypeScript, Rust) across 4 compilers (Go, TinyGo, Rust, AssemblyScript). Setting up a development environment for this polyglot stack is complex, and keeping it updated is even harder, unless you have the right tools. For this iteration, I have selected &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt; as the default tool for &lt;a href=&quot;blog/version-0.3.0.html#gitroot-bets-on-mise&quot;&gt;managing the GitRoot dev environment&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Beyond technical features, a new focus has been placed on &lt;a href=&quot;blog/version-0.3.0.html#communication-and-community-building&quot;&gt;communication&lt;/a&gt;. You can now follow daily progress on our new fediverse instance (@forge@gitroot.dev), engage in direct chat on matrix (#gitroot:matrix.org), and read a new blog post detailing concrete ways to contribute today.&lt;/p&gt;

&lt;p&gt;Finally, I discuss &lt;a href=&quot;blog/version-0.3.0.html#apex-a-better-web-experience&quot;&gt;Apex&lt;/a&gt; and many &lt;a href=&quot;blog/version-0.3.0.html#and-others-useful-updates&quot;&gt;other tiny but useful things&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;in-depth-details&quot;&gt;In-depth Details&lt;/h2&gt;

&lt;h3 id=&quot;for-admin-activating-an-executor&quot;&gt;For admin: activating an executor&lt;/h3&gt;

&lt;p&gt;When you update GitRoot to &lt;code&gt;0.3.0&lt;/code&gt; a new configuration block will appear in the &lt;code&gt;.gitroot/forgeConfig.yml&lt;/code&gt; file of your &lt;code&gt;root&lt;/code&gt; repository:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;execconf&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;baremetal&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;bwrap&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;user&lt;/span&gt;: nobody
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;uid&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;65534&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;gid&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;65534&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;robind&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;bind&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;container&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;bin&lt;/span&gt;: podman
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;ssh&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;hosts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;user&lt;/span&gt;: myUSer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;address&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;127.0.0.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;port&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;22&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;publickey&lt;/span&gt;: ssh-ed25519 AAAAC3NzaC1...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can enable multiple executors, but GitRoot will only use the first one enabled, checking them in this order: &lt;code&gt;bareMetal&lt;/code&gt;, &lt;code&gt;bwrap&lt;/code&gt;, &lt;code&gt;container&lt;/code&gt; and then &lt;code&gt;ssh&lt;/code&gt;. If you activate an executor, remember to also add the &lt;code&gt;hop&lt;/code&gt; plugin, as it is the only known plugin designed to utilize them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;bare-metal-simplest-but-most-dangerous&quot;&gt;Bare Metal: simplest but most dangerous&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;bareMetal&lt;/code&gt; executor runs commands directly on the host machine. It requires no external dependencies and should work on any hardware. Be extremely cautious: users of your GitRoot instance will gain the ability to execute arbitrary code on the host.&lt;/p&gt;

&lt;h4 id=&quot;bubblewrap-bwrap-lightweight-sandboxing&quot;&gt;Bubblewrap (bwrap): lightweight sandboxing&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;bwrap&lt;/code&gt; executor introduces a lightweight container (sandbox) between executed commands and the host, similar to the technology used by &lt;a target=&quot;_blank&quot; href=&quot;https://flatpak.org/&quot;&gt;flatpack&lt;/a&gt;. To use it, you must install the &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/containers/bubblewrap&quot;&gt;bubblewrap&lt;/a&gt; application.&lt;/p&gt;

&lt;p&gt;By default, GitRoot configures &lt;code&gt;bwrap&lt;/code&gt; to use the &lt;code&gt;nobody&lt;/code&gt; user for restriction, though this can be changed. You can also define bindings between your host and the commands running inside the container. For example, &lt;code&gt;robind: [&amp;quot;/var/log/caddy/access.log&amp;quot;]&lt;/code&gt; would allow a job to execute &lt;code&gt;cat /var/log/caddy/access.log&lt;/code&gt;.&lt;/p&gt;

&lt;h4 id=&quot;container-heavyweight-but-most-flexible&quot;&gt;Container: heavyweight but most flexible&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;container&lt;/code&gt; executor enables the execution of commands within &lt;a target=&quot;_blank&quot; href=&quot;https://podman.io/&quot;&gt;podman&lt;/a&gt; or &lt;a target=&quot;_blank&quot; href=&quot;https://www.docker.com/&quot;&gt;docker&lt;/a&gt; containers, allowing your users to run any OCI image. The only requirement for the administrator is to install either &lt;a target=&quot;_blank&quot; href=&quot;https://podman.io/docs/installation&quot;&gt;podman&lt;/a&gt; or &lt;a target=&quot;_blank&quot; href=&quot;https://www.docker.com/&quot;&gt;docker&lt;/a&gt;. Remember to change the &lt;code&gt;bin&lt;/code&gt; property to &lt;code&gt;docker&lt;/code&gt; if that is your chosen runtime.&lt;/p&gt;

&lt;p&gt;GitRoot includes a useful feature for containers: if the user provides a path to a &lt;code&gt;Containerfile&lt;/code&gt; or &lt;code&gt;Dockerfile&lt;/code&gt;, GitRoot will automatically build the image before running commands inside it.&lt;/p&gt;

&lt;h4 id=&quot;ssh-remote-execution-alpha&quot;&gt;SSH: remote execution (alpha)&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;ssh&lt;/code&gt; executor allows commands to be executed on a remote host via a standard SSH connection. You simply provide the standard SSH connection details. This executor should be considered the most alpha of all options (and yes, all others are also considered alpha).&lt;/p&gt;

&lt;h3 id=&quot;for-user-using-the-hop-plugin&quot;&gt;For user: using the &lt;code&gt;hop&lt;/code&gt; plugin&lt;/h3&gt;

&lt;p&gt;To execute a build (or command execution), users must activate the &lt;code&gt;hop&lt;/code&gt; plugin and configure its execution rules:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;url&lt;/span&gt;: /home/rmaneschi/projects/gitroot/app/testsuite/../plugins/hop/hop-0.0.1.wasm
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: hop
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;version&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;0.0.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;active&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          - &lt;span style=&quot;color:#ff79c6&quot;&gt;command&lt;/span&gt;: cat
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          &lt;span style=&quot;color:#ff79c6&quot;&gt;build&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          &lt;span style=&quot;color:#ff79c6&quot;&gt;cmds&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            - &lt;span style=&quot;color:#ff79c6&quot;&gt;args&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;                - README.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;              &lt;span style=&quot;color:#ff79c6&quot;&gt;cmd&lt;/span&gt;: cat
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          &lt;span style=&quot;color:#ff79c6&quot;&gt;env&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add all desired commands for the plugin to call in &lt;code&gt;run.write.exec&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then in &lt;code&gt;run.configuration.exec&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;set environment variables via &lt;code&gt;env&lt;/code&gt; (e.g., &lt;code&gt;&amp;quot;MY_ENV=my_val&amp;quot;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;specify the &lt;code&gt;build&lt;/code&gt; property (only for the &lt;code&gt;container&lt;/code&gt; executor) to use an existing image (e.g., &lt;code&gt;docker.io/bash:5.3.3&lt;/code&gt;) or to build one from a file (e.g., &lt;code&gt;/my/path/to/Containerfile&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;define the commands and arguments to execute under &lt;code&gt;cmds&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;plugin-api-report-and-reporter&quot;&gt;Plugin api report and reporter&lt;/h3&gt;

&lt;p&gt;Occasionally, plugins need to communicate important information to the user. For instance, the &lt;code&gt;ladybug&lt;/code&gt; plugin might need to report invalid metadata. In traditional forges, a web interface serves this purpose, but GitRoot operates without a fixed UI.&lt;/p&gt;

&lt;p&gt;Past considerations included sending emails (a solution often disliked by developers) or leveraging external communication methods like RSS via the &lt;code&gt;pollen&lt;/code&gt; plugin (introduced in v0.2.0) or the Fediverse via a Go-to-Social instance. As always, GitRoot aims to provide options rather than dictate a single solution.&lt;/p&gt;

&lt;p&gt;To allow reporting without forcing a communication channel, I&amp;rsquo;ve created two new plugin APIs.&lt;/p&gt;

&lt;p&gt;Report: plugins call this API with a severity level (&lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, or &lt;code&gt;error&lt;/code&gt;) and an array of strings to display. GitRoot stacks all reports for every commit and plugin.&lt;/p&gt;

&lt;p&gt;Reporter API: after reports are collected, GitRoot calls all plugins that implement the &lt;code&gt;Reporter&lt;/code&gt; interface.&lt;/p&gt;

&lt;p&gt;Currently, the &lt;code&gt;grafter&lt;/code&gt; plugin is the only known reporter; it adds the collected reports to the graft output. This two new API opens up a wide range of possibilities for future versions. I have several ideas, but I encourage you to &lt;a href=&quot;contact.html&quot;&gt;share yours&lt;/a&gt;!&lt;/p&gt;

&lt;h3 id=&quot;gitroot-bets-on-mise&quot;&gt;GitRoot bets on mise&lt;/h3&gt;

&lt;p&gt;Managing a diverse development environment that spans Go, TypeScript, and Rust can be a significant hurdle. In this iteration, I have standardized on mise as the official tool for managing the GitRoot dev environment. This choice simplifies installation and ensures all dependencies remain correctly updated for development.&lt;/p&gt;

&lt;p&gt;This choice seems to be a good one, as we welcome a new contributor who has already integrated some common tasks using &lt;code&gt;mise&lt;/code&gt;. Thanks to their efforts, we can now simply run &lt;code&gt;mise run build&lt;/code&gt; to compile GitRoot or &lt;code&gt;mise run test&lt;/code&gt; to execute all tests.&lt;/p&gt;

&lt;h3 id=&quot;communication-and-community-building&quot;&gt;Communication and community building&lt;/h3&gt;

&lt;p&gt;Alongside the technical work for v0.3.0, a focused effort was put into non-technical tasks to improve communication with the community.&lt;/p&gt;

&lt;p&gt;To allow you to follow daily progress and current thoughts, I have set up a &lt;a target=&quot;_blank&quot; href=&quot;https://gotosocial.org/&quot;&gt;GoToSocial&lt;/a&gt; (Mastodon) instance. You can find the official account at &lt;code&gt;@forge@gitroot.dev&lt;/code&gt; or directly at [&lt;a target=&quot;_blank&quot; href=&quot;https://gts.git                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               he&quot;&gt;https://gts.git                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               he&lt;/a&gt; parameter�        shaddr&lt;code&gt;(default value&lt;/code&gt;ssh://localhost:4545/&lt;code&gt;) and&lt;/code&gt;externalhttpaddr&lt;code&gt;(default value&lt;/code&gt;&lt;a target=&quot;_blank&quot; href=&quot;http://localhost:4546/`&quot;&gt;http://localhost:4546/`&lt;/a&gt;) have been added to ensure the display of correct external urls to users. These addresses are notably used by the &lt;code&gt;apex&lt;/code&gt; plugin generate proper public links. They are kept separate from the internal &lt;code&gt;sshaddr&lt;/code&gt; and &lt;code&gt;httpaddr&lt;/code&gt; because your GitRoot instance might be configured to listen internally (on &lt;code&gt;127.0.0.1&lt;/code&gt;) while being reachable externally through a reverse proxy.&lt;/p&gt;

&lt;h4 id=&quot;integrated-updater&quot;&gt;Integrated updater&lt;/h4&gt;

&lt;p&gt;I&amp;rsquo;ve added a preliminary implementation for an integrated updater. This feature will handle breaking changes gracefully. For this release, I&amp;rsquo;ve added some new parameters to &lt;code&gt;forgeConf.yml&lt;/code&gt;, the integrated updater will handle these by setting standard values that should not affect your current user instance setup.&lt;/p&gt;

&lt;h4 id=&quot;rust-and-wasm&quot;&gt;Rust and WASM&lt;/h4&gt;

&lt;p&gt;While integrating Rust, I discovered that the WASM (and specifically WASI Preview 1) target does not permit exporting the standard &lt;code&gt;free&lt;/code&gt; and &lt;code&gt;malloc&lt;/code&gt; functions, which GitRoot requires. I&amp;rsquo;ve added an option to rename these functions to &lt;code&gt;gitrootAlloc&lt;/code&gt; and &lt;code&gt;gitrootFree&lt;/code&gt;, resolving this compatibility issue.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.3.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.2.0.md</link><title>0.2.0</title><pubDate>2025/09/18</pubDate><description>&lt;h1 id=&quot;version-0-2-0-is-here&quot;&gt;Version 0.2.0 is here&lt;/h1&gt;

&lt;p&gt;In this version, my main focus was on stabilization. I fixed over 14 issues, some of which were identified before version 0.1.0, but a significant proportion were reported by YOU. A big thanks to everyone who took part of it in any way.&lt;/p&gt;

&lt;p&gt;Every time I shared gitroot, I came across new issues with the documentation and binaries, but also questions such as &amp;ldquo;how can I compile it?&amp;rdquo; or &amp;ldquo;what&amp;rsquo;s the next step?&amp;rdquo;. And I&amp;rsquo;m delighted to see your enthusiasm for it!&lt;/p&gt;

&lt;h2 id=&quot;first-contribution&quot;&gt;First contribution&lt;/h2&gt;

&lt;p&gt;Although all my new contributions were related to &amp;ldquo;bug fixes&amp;rdquo;, some of them required new features.&lt;/p&gt;

&lt;h3 id=&quot;no-notification-no-contribution&quot;&gt;No notification == no contribution&lt;/h3&gt;

&lt;p&gt;The first amusing problem came from the first contributor! Fifteen days later, he contacted me to say, &amp;ldquo;Hi, I pushed a branch where I corrected a lot of typos, but&amp;hellip; nothing has happened since I pushed it?&amp;rdquo;. In fact, I wasn&amp;rsquo;t actually aware that a new branch had been pushed. I had forgotten to implement a mechanism that would notify me whenever a new contribution is published!&lt;/p&gt;

&lt;p&gt;A simple way to achieve this is to add an RSS feed (yes, I was inspired by Radicle for this feature). This is ideal, because all I have to do is add an &amp;ldquo;rss.xml&amp;rdquo; file to the web folder&amp;hellip;and the current API of plugin 0.1.0 allows me to do this! So I created the &lt;code&gt;pollen&lt;/code&gt; plugin. It takes all the commits from all branches and adds an &lt;code&gt;rss:item&lt;/code&gt; to a page.&lt;/p&gt;

&lt;p&gt;However, in order to achieve this, I opted to experiment with a different programming language (all the plugins are developed in Golang). I chose &lt;code&gt;assemblyscript&lt;/code&gt;, which is a subset of the &lt;code&gt;typescript&lt;/code&gt; language. So I added a new &lt;code&gt;plugin sdk&lt;/code&gt; in typescript.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To follow all gitroot commits follow &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/rss/all-commits.xml&quot;&gt;https://gitroot.dev/rss/all-commits.xml&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;lot-of-bugs&quot;&gt;Lot of bugs&lt;/h3&gt;

&lt;p&gt;During this first contribution, I discovered numerous bugs in the “grafter” plugin. From the &lt;a href=&quot;issues/close/v0.2.0/73e8-graft-diff-not-working.html&quot;&gt;diff rendering&lt;/a&gt; to &lt;a href=&quot;issues/close/v0.2.0/e188-graft-merge-not-wanted.html&quot;&gt;unwanted merging&lt;/a&gt; and &lt;a href=&quot;issues/close/9422-graft-need-3-review.html&quot;&gt;very complicated final merging&lt;/a&gt;, nothing worked! Even the basic GitRoot features were painful to use :D&lt;/p&gt;

&lt;h2 id=&quot;let-s-the-show-continue&quot;&gt;Let&amp;rsquo;s the show continue&lt;/h2&gt;

&lt;h3 id=&quot;no-linux-no-gitroot&quot;&gt;No linux == no gitroot&lt;/h3&gt;

&lt;p&gt;Surprisingly, someone wanted to compile gitroot on a Mac. Well, gitroot is actually written in Go, so there&amp;rsquo;s no problem doing that&amp;hellip; Except that I forgot to publish a fix I made to a dependency. Go couldn&amp;rsquo;t download the right dependency and therefore fails to compile completely&amp;hellip; But everything is OK now! I added an automatic compilation for Mac, you can find this on the &lt;a href=&quot;CHANGELOG.html&quot;&gt;version&lt;/a&gt; page.&lt;/p&gt;

&lt;h3 id=&quot;doc-doc-doc&quot;&gt;Doc, doc&amp;hellip; doc?&lt;/h3&gt;

&lt;p&gt;After that, someone tried to run gitroot on their local computer, as described in the &lt;a href=&quot;doc/tutorials/from_zero_to_hero.html&quot;&gt;document&lt;/a&gt;. However, when version &lt;code&gt;0.1.0&lt;/code&gt; was released, I discovered a bug. Gitroot cannot know where the &lt;code&gt;root&lt;/code&gt; repository is located (because you can change its name). To fix this, I added a configuration file before initialization, which had to be passed on launch&amp;hellip; It&amp;rsquo;s my fault, I forgot to update this tutorial.&lt;/p&gt;

&lt;h3 id=&quot;a-lot-more&quot;&gt;A lot more&lt;/h3&gt;

&lt;p&gt;As you can see, there were many mistakes in the first version. However, they have all been fixed, and, of course, I have added plenty more to keep me busy for the next version!&lt;/p&gt;

&lt;h3 id=&quot;more-technicals-bugs&quot;&gt;More technicals bugs&lt;/h3&gt;

&lt;p&gt;But I also fixed several important issues that were the result of some shortcuts that every project has to take for publication. One was about a &lt;a href=&quot;issues/close/v0.2.0/95b0-plugins-fs-bug.html&quot;&gt;FS bug&lt;/a&gt; when directories were empty, another concerned the &lt;a href=&quot;issues/close/v0.2.0/f1ab-garbage-collecte-plugins.html&quot;&gt;garbage collect plugin&lt;/a&gt; when the plugin had finished running, and many others.&lt;/p&gt;

&lt;h2 id=&quot;simplify-plugin-install&quot;&gt;Simplify plugin install&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;0.1.0&lt;/code&gt; version, to install a plugin you should download it, tell to gitroot where it is and then after push/pull activate/configure it. Now it is way more simple, just give an url, like &lt;code&gt;https://gitroot.dev/releases/0.2.0/pollen-0.0.1.wasm&lt;/code&gt; and optionnally (but encouraged) a checksum &lt;code&gt;sha256: 49cd8e1846b1612a0d5686918b502fe29e070e7bf9613c1ba9a1dfe5417d0f2a&lt;/code&gt;. Gitroot will download, check and run it. Of course you need to activate it. All url and checksum are in the &lt;a href=&quot;CHANGELOG.html&quot;&gt;versions page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have added a mecanisme to check that the plugin you try to run is compatble with your version of GitRoot. So launching &lt;code&gt;ladybug-0.0.1.wasm&lt;/code&gt; in &lt;code&gt;gitroot-0.2.0&lt;/code&gt; will log an error and not run the wasm. As GitRoot is in alpha version all new gitroot version will need plugins update.&lt;/p&gt;

&lt;h2 id=&quot;what-next&quot;&gt;What next?&lt;/h2&gt;

&lt;p&gt;With the exception of any new bugs detected for the next iteration, I would like to focus on the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;creating a plugins marketplace&lt;/li&gt;
&lt;li&gt;adding a new language SDK for plugins (Maybe Rust? Or Zig?)&lt;/li&gt;
&lt;li&gt;rebasing the Git lib fork and applying patches&lt;/li&gt;
&lt;/ul&gt;
</description><guid>https://gitroot.dev//blog/version-0.2.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.1.0.md</link><title>0.1.0</title><pubDate>2025/09/18</pubDate><description>&lt;h1 id=&quot;gitroot-0-1-0-is-deployed&quot;&gt;GitRoot 0.1.0 is deployed 🎉��&lt;/h1&gt;

&lt;h2 id=&quot;gitroot-in-numbers&quot;&gt;GitRoot in numbers:&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;58 commits&lt;/li&gt;
&lt;li&gt;+500 files &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/&quot;&gt;https://gitroot.dev/worktree/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;1 issue in gitlab (closed) &lt;a target=&quot;_blank&quot; href=&quot;https://gitlab.com/gitroot/gitroot/-/issues/1&quot;&gt;https://gitlab.com/gitroot/gitroot/-/issues/1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;29 issues close &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/boards/closed.html&quot;&gt;https://gitroot.dev/boards/closed.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;43 issues open &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/boards/issues.html&quot;&gt;https://gitroot.dev/boards/issues.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;22 pages de doc &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/doc/&quot;&gt;https://gitroot.dev/doc/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;4 licences &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/README.html#license&quot;&gt;https://gitroot.dev/README.html#license&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;4 plugins &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/CHANGELOG.html#plugins&quot;&gt;https://gitroot.dev/CHANGELOG.html#plugins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But if you retain only one, it is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 contributor &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/.gitroot/users.yml.html&quot;&gt;https://gitroot.dev/worktree/.gitroot/users.yml.html&lt;/a&gt; (others are forge and plugins 😭)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So there is only one thig to do&amp;hellip; read the doc and contribute!&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.1.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/security-vulnerability-1.md</link><title>blog/security-vulnerability-1.md</title><pubDate>2026/03/30</pubDate><description>&lt;h1 id=&quot;security-advisory-executor-command-injection&quot;&gt;Security Advisory: Executor Command Injection&lt;/h1&gt;

&lt;h2 id=&quot;1-executive-summary&quot;&gt;1. Executive Summary&lt;/h2&gt;

&lt;p&gt;A vulnerability has been identified within the GitRoot &lt;strong&gt;executors&lt;/strong&gt; system. Under specific conditions, it is possible for a malicious actor to execute unauthorized commands on the host machine. Given the complexity of the required conditions, this vulnerability is not easily exploitable in a standard production environment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Severity:&lt;/strong&gt; Moderate&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Affected Version:&lt;/strong&gt; 0.3.0&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fix Status:&lt;/strong&gt; Scheduled for version 0.4.0&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;2-technical-details&quot;&gt;2. Technical Details&lt;/h2&gt;

&lt;p&gt;The vulnerability stems from the way executors validate commands defined by plugins. Currently, a plugin can declare a benign command in its manifest (e.g., &lt;code&gt;cat&lt;/code&gt;), but execute an arbitrary command during runtime (e.g., &lt;code&gt;rm -rf data/&lt;/code&gt;). GitRoot currently lacks a strict enforcement mechanism to verify that the executed binary matches the declared command.&lt;/p&gt;

&lt;h2 id=&quot;3-impact-analysis&quot;&gt;3. Impact Analysis&lt;/h2&gt;

&lt;p&gt;The level of risk depends on your infrastructure configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;High Risk:&lt;/strong&gt; Users utilizing &lt;code&gt;bareMetal&lt;/code&gt; or &lt;code&gt;ssh&lt;/code&gt; executors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Low Risk:&lt;/strong&gt; Users utilizing &lt;code&gt;bubblewrap&lt;/code&gt; or &lt;code&gt;container&lt;/code&gt; isolation (as the sandbox significantly limits the potential impact on the host).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exploitability Constraints:&lt;/strong&gt;
This vulnerability is not a &amp;ldquo;remote code execution&amp;rdquo; flaw in the traditional sense. Exploitation requires:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Installing a malicious plugin.&lt;/li&gt;
&lt;li&gt;Having write access to the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt; of the root repository.&lt;/li&gt;
&lt;li&gt;Merging a malicious &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt; into the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Currently, creating plugins is undocumented, and the only known plugin using executors is &lt;code&gt;hop&lt;/code&gt;. Since any modification to &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt; requires a merge into the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt;, a standard code review process should easily detect and prevent such malicious entries.&lt;/p&gt;

&lt;h2 id=&quot;4-immediate-mitigations&quot;&gt;4. Immediate Mitigations&lt;/h2&gt;

&lt;p&gt;If you cannot wait for the 0.4.0 release, we recommend applying the following measures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Disable Executors:&lt;/strong&gt; Set your forge configuration for executors to &lt;code&gt;None&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secure Executors:&lt;/strong&gt; Use more secure executors, such as &lt;code&gt;bwrap&lt;/code&gt; or &lt;code&gt;container&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plugin Trust:&lt;/strong&gt; Only install and enable plugins from trusted sources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access Control:&lt;/strong&gt; Strictly monitor and review all changes to &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt;, specifically focusing on authorized system commands.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;5-patch-schedule&quot;&gt;5. Patch Schedule&lt;/h2&gt;

&lt;p&gt;This vulnerability will be fully addressed in &lt;strong&gt;version 0.4.0&lt;/strong&gt;.
We have decided not to release an intermediate 0.3.1 patch, as the attack vector relies on an &amp;ldquo;insider&amp;rdquo; or &amp;ldquo;compromised account&amp;rdquo; scenario that is effectively mitigated by standard code review practices.&lt;/p&gt;

&lt;h2 id=&quot;6-security-contact&quot;&gt;6. Security Contact&lt;/h2&gt;

&lt;p&gt;Transparency and security are core values at GitRoot. If you discover any other potential vulnerabilities or have questions regarding this report, please contact us directly at &lt;a href=&quot;blog/contact@gitroot.dev&quot;&gt;contact@gitroot.dev&lt;/a&gt;.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/security-vulnerability-1.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/modifying-API-SDK-plugin.md</link><title>Modifying the API in the SDK Plugin</title><pubDate>2026/04/06</pubDate><description>&lt;h1 id=&quot;modifying-the-api-in-the-sdk-plugin&quot;&gt;Modifying the API in the SDK Plugin&lt;/h1&gt;

&lt;p&gt;The last feature for version 0.4 is about adding &lt;code&gt;mv&lt;/code&gt; (move) and &lt;code&gt;cp&lt;/code&gt; (copy) functions to the SDK plugin. I&amp;rsquo;m taking advantage of this development to explain how the plumbing works under the hood.&lt;/p&gt;

&lt;p&gt;Right now, the SDK plugin is undocumented because there are a lot of breaking changes coming. Every time I develop a new plugin, I discover better ways to do things, and the SDK needs to adapt.&lt;/p&gt;

&lt;p&gt;For example, when I added &lt;code&gt;apex_code&lt;/code&gt; (a plugin that takes code as a string and returns syntax-highlighted HTML), I wondered: &amp;ldquo;Is this a standalone plugin?&amp;rdquo;. Not really, because there&amp;rsquo;s no file to manipulate, neither in input nor output. Instead, it manages blocks inside an existing file. The SDK wasn&amp;rsquo;t originally built with that use case in mind.&lt;/p&gt;

&lt;p&gt;Anyway, I think explaining how the SDK works through a concrete example will be helpful, if not for you, at least for me.&lt;/p&gt;

&lt;h2 id=&quot;what-is-the-sdk-s-job&quot;&gt;What is the SDK&amp;rsquo;s job?&lt;/h2&gt;

&lt;p&gt;The SDK exists to pass data from GitRoot (the host) to the plugin (the guest). As you know, GitRoot uses WebAssembly (WASM) to manage plugins. Unfortunately, WASM is still a bit low-level to implement: between the host and the guest, only integers can travel.&lt;/p&gt;

&lt;p&gt;Wait, only integers?&lt;/p&gt;

&lt;p&gt;Yes. So, how do we send a string? The host can store any kind of data in the guest&amp;rsquo;s memory. GitRoot stores a string, retrieves the pointer (the memory address) and the size, and sends those two integers to the guest. The guest then retrieves the string from its own memory. It&amp;rsquo;s a complex dance just for a simple string. The SDK is here to hide all that mess.&lt;/p&gt;

&lt;p&gt;When you build a plugin, you just implement an interface. The SDK handles the rest: you receive objects and strings, and you send back data without ever worrying about the underlying WASM pointers.&lt;/p&gt;

&lt;h2 id=&quot;what-does-a-concrete-implementation-look-like&quot;&gt;What does a concrete implementation look like?&lt;/h2&gt;

&lt;p&gt;When I add a new function, I always start with the host side. All functions are located in &lt;a href=&quot;app/server/plugin/runtime.go&quot;&gt;runtime.go&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We need to register the function in &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/wazero/wazero&quot;&gt;wazero&lt;/a&gt;, our WASM runtime. I add two versions because I haven&amp;rsquo;t found a clean way to bridge the difference between AssemblyScript and Go/Rust yet. Hopefully, I&amp;rsquo;ll figure that out one day.&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;r.wazRun.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#50fa7b&quot;&gt;NewFunctionBuilder&lt;/span&gt;().&lt;span style=&quot;color:#50fa7b&quot;&gt;WithFunc&lt;/span&gt;(r.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(&lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;)).&lt;span style=&quot;color:#50fa7b&quot;&gt;Export&lt;/span&gt;(&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;moveFile&amp;#34;&lt;/span&gt;).
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#50fa7b&quot;&gt;NewFunctionBuilder&lt;/span&gt;().&lt;span style=&quot;color:#50fa7b&quot;&gt;WithFunc&lt;/span&gt;(r.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(&lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;)).&lt;span style=&quot;color:#50fa7b&quot;&gt;Export&lt;/span&gt;(&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;moveFileAS&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I implement the builder:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(forAS &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;interface&lt;/span&gt;{} {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; !forAS {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, ptr, size, ptr2, size2 &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#6272a4&quot;&gt;// from ptr/size to string or object
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;			r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromPath, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	} &lt;span style=&quot;color:#ff79c6&quot;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, ptr, ptr2 &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#6272a4&quot;&gt;// from ptr to string or object
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;			r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromPath, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Yeah, I know. As a Gopher, seeing &lt;code&gt;interface{}&lt;/code&gt; as a return type and all these uint32 feels wrong. But hey, we&amp;rsquo;re not here for a code review, we&amp;rsquo;re here to add an API. If you have a cleaner way, feel free to submit a graft!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We want to allow any plugin to move a file. Let&amp;rsquo;s define the internal &lt;code&gt;moveFile&lt;/code&gt; function:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromPath, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#6272a4&quot;&gt;//need to do something...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Actually, there&amp;rsquo;s a catch, GitRoot uses three internal filesystems: one for the git worktree, one for web content, and one for the cache. Even if they are abstracted under one root, the code often expects a specific FS. I&amp;rsquo;ll probably clean this up later, but for now:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#6272a4&quot;&gt;//need to do something...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now your plugin can call &lt;code&gt;moveFile(&amp;quot;cache&amp;quot;, &amp;quot;artifacts/myBin&amp;quot;, &amp;quot;web&amp;quot;, &amp;quot;releases/myBin-v1.0&amp;quot;)&lt;/code&gt; to publish an artifact, or &lt;code&gt;moveFile(&amp;quot;web&amp;quot;, &amp;quot;index.html&amp;quot;, &amp;quot;web&amp;quot;, &amp;quot;old/index.html&amp;quot;)&lt;/code&gt; to back up a file.&lt;/p&gt;

&lt;p&gt;I won&amp;rsquo;t bore you with the implementation logic here. If you&amp;rsquo;re curious, check the &lt;a href=&quot;app/server/plugin/runtime.go&quot;&gt;code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is how the final builder looks, converting pointers to strings:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(forAS &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;interface&lt;/span&gt;{} {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; !forAS {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, fromFsPtr, fromFsSize, fromPathPtr, fromGlobPathSize, toFsPtr, toFsSize, toPathPtr, toPathSize &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;uint64&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile fromFs&amp;#34;&lt;/span&gt;, fromFsPtr, fromFsSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile fromGlobPath&amp;#34;&lt;/span&gt;, fromPathPtr, fromGlobPathSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile toFs&amp;#34;&lt;/span&gt;, toFsPtr, toFsSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile toPath&amp;#34;&lt;/span&gt;, toPathPtr, toPathSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	} &lt;span style=&quot;color:#ff79c6&quot;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, fromFsPtr, fromPathPtr, toFsPtr, toPathPtr &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;uint64&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS fromFs&amp;#34;&lt;/span&gt;, fromFsPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS fromGlobPath&amp;#34;&lt;/span&gt;, fromPathPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS toFs&amp;#34;&lt;/span&gt;, toFsPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS toPath&amp;#34;&lt;/span&gt;, toPathPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L38&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L38&quot;&gt;38&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L39&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L39&quot;&gt;39&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L40&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L40&quot;&gt;40&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L41&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L41&quot;&gt;41&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L42&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L42&quot;&gt;42&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L43&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L43&quot;&gt;43&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When I need to pass complex objects, I use &lt;code&gt;json.Marshal&lt;/code&gt; in GitRoot and &lt;code&gt;json.Unmarshal&lt;/code&gt; in the SDK. It works, but don&amp;rsquo;t get too attached to it, I might change the serialization method in the future.&lt;/p&gt;

&lt;h2 id=&quot;inside-the-sdk&quot;&gt;Inside the SDK&lt;/h2&gt;

&lt;p&gt;Now that the host is ready, we need to implement the other side: the SDK itself. I usually start with Go, then move to Rust and AssemblyScript. Sometimes I forget the others&amp;hellip; shame on me! I&amp;rsquo;m planning to automate this with some boilerplate generation later.&lt;/p&gt;

&lt;p&gt;In the &lt;a href=&quot;app/libs/golang/plugin/model/server.go&quot;&gt;server&lt;/a&gt; interface:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;type&lt;/span&gt; Server &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;interface&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#6272a4&quot;&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;    &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#6272a4&quot;&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make it more &amp;ldquo;user-friendly&amp;rdquo;, I use a struct to represent the filesystem in &lt;a href=&quot;app/libs/golang/plugin/model/fs.go&quot;&gt;model/fs.go&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (g &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;GrFs) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(fromPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; g.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(g.base, fromPath, g.base, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (g &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;GrFs) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileToFs&lt;/span&gt;(fromPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, toFs &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;GrFs, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; g.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(g.base, fromPath, toFs.base, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The actual WASM call happens in &lt;a href=&quot;app/libs/golang/plugin/server.go&quot;&gt;plugin/server.go&lt;/a&gt;, where we transform strings back into pointers:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;// MoveFile implements [model.ServerNeeded].
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (s &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;pServer) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(fromFs model.FsBase, fromPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, toFs model.FsBase, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr, size &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;string&lt;/span&gt;(fromFs))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr2, size2 &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(fromPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr3, size3 &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;string&lt;/span&gt;(toFs))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr4, size4 &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	resPtrSize &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;_moveFile&lt;/span&gt;(ptr, size, ptr2, size2, ptr3, size3, ptr4, size4)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(fromFs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(fromPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(toFs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;ptrSizeToError&lt;/span&gt;(resPtrSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally, the link to the host via &lt;code&gt;go:wasmimport&lt;/code&gt; in &lt;a href=&quot;app/libs/golang/plugin/imports.go&quot;&gt;plugin/imports.go&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;//go:wasmimport gitroot moveFile
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;_moveFile&lt;/span&gt;(fromFsPtr, fromFsSize, fromPathPtr, fromPathSize, toFsPtr, toFsSize, toPathPtr, toPathSize &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) (ptrSize &lt;span style=&quot;color:#8be9fd&quot;&gt;uint64&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;how-to-use-it&quot;&gt;How to use it?&lt;/h2&gt;

&lt;p&gt;Now, any plugin can use this. Here&amp;rsquo;s a simple Go plugin that triggers a build whenever a &lt;code&gt;.go&lt;/code&gt; file is modified and moves the binary to the web folder.&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;package&lt;/span&gt; main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;path/filepath&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;strings&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	gitroot &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;gitroot.dev/libs/golang/plugin&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;gitroot.dev/libs/golang/plugin/model&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;var&lt;/span&gt; defaultRun = []model.PluginRun{{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	Path:   &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*.go&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	When:   model.PluginRunWhenAll,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	Branch: []&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;{&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	Write: model.PluginWrite{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		Web: []model.PluginWriteRight{{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			Path: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;releases/main&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			Can:  model.PluginWriteRightCanAll,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;type&lt;/span&gt; Plugin &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	server model.Server
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    needToBuild &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;Init&lt;/span&gt;(repoName &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, confHasChanged &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;, serializedConf &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;StartCommit&lt;/span&gt;(commit model.Commit) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                  { &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;AddFile&lt;/span&gt;(file model.File) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L38&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L38&quot;&gt;38&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L39&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L39&quot;&gt;39&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L40&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L40&quot;&gt;40&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L41&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L41&quot;&gt;41&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;ModFile&lt;/span&gt;(file model.File) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L42&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L42&quot;&gt;42&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L43&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L43&quot;&gt;43&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L44&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L44&quot;&gt;44&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L45&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L45&quot;&gt;45&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L46&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L46&quot;&gt;46&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;DelFile&lt;/span&gt;(file model.File) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L47&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L47&quot;&gt;47&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L48&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L48&quot;&gt;48&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L49&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L49&quot;&gt;49&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L50&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L50&quot;&gt;50&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L51&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L51&quot;&gt;51&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;EndCommit&lt;/span&gt;(commit model.Commit) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                    { &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L52&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L52&quot;&gt;52&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L53&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L53&quot;&gt;53&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;Finish&lt;/span&gt;() &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L54&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L54&quot;&gt;54&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; p.needToBuild {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L55&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L55&quot;&gt;55&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        res, _ &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; p.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;Exec&lt;/span&gt;(model.Exec{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L56&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L56&quot;&gt;56&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            Cmds: []model.Cmd{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L57&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L57&quot;&gt;57&lt;/a&gt;&lt;/span&gt;&lt;span&gt;                {Cmd: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;go&amp;#34;&lt;/span&gt;, Args: []&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;{&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;build&amp;#34;&lt;/span&gt;}},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L58&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L58&quot;&gt;58&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L59&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L59&quot;&gt;59&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            Artifacts: []&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;{&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L60&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L60&quot;&gt;60&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L61&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L61&quot;&gt;61&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; p.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;Cache&lt;/span&gt;().&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileToFs&lt;/span&gt;(res.Artifacts[&lt;span style=&quot;color:#bd93f9&quot;&gt;0&lt;/span&gt;], p.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;Webcontent&lt;/span&gt;(), filepath.&lt;span style=&quot;color:#50fa7b&quot;&gt;Join&lt;/span&gt;(&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;releases&amp;#34;&lt;/span&gt;, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L62&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L62&quot;&gt;62&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L63&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L63&quot;&gt;63&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L64&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L64&quot;&gt;64&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L65&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L65&quot;&gt;65&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L66&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L66&quot;&gt;66&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;Build&lt;/span&gt;(server model.Server) model.Plugin {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L67&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L67&quot;&gt;67&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;&amp;amp;&lt;/span&gt;Plugin{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L68&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L68&quot;&gt;68&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		server: server,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L69&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L69&quot;&gt;69&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L70&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L70&quot;&gt;70&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L71&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L71&quot;&gt;71&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L72&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L72&quot;&gt;72&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;//go:wasmexport install
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L73&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L73&quot;&gt;73&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L74&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L74&quot;&gt;74&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	gitroot.&lt;span style=&quot;color:#50fa7b&quot;&gt;Register&lt;/span&gt;(defaultRun, Build)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L75&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L75&quot;&gt;75&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;is-that-all&quot;&gt;Is that all?&lt;/h2&gt;

&lt;p&gt;Nope. I still need to update the &lt;a href=&quot;app/libs/rust/plugin/src/fs.rs&quot;&gt;Rust&lt;/a&gt; and &lt;a href=&quot;app/libs/ts/plugin/fs.ts&quot;&gt;AssemblyScript&lt;/a&gt; SDKs and, of course, add &lt;a href=&quot;app/libs/golang/plugin/test/fakeserver.go&quot;&gt;tests&lt;/a&gt;. But this should give you a good idea of how the plumbing works.&lt;/p&gt;

&lt;p&gt;If anything is unclear, &lt;a href=&quot;contact.html&quot;&gt;let me know&lt;/a&gt;!&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/modifying-API-SDK-plugin.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/how-to-help_fr_11-2025.md</link><title>Comment contribuer au développement de GitRoot</title><pubDate>2025/11/18</pubDate><description>&lt;h1 id=&quot;comment-contribuer-au-développement-de-gitroot-11-2025&quot;&gt;Comment contribuer au développement de GitRoot 11-2025&lt;/h1&gt;

&lt;p&gt;Je viens tout juste de lire &lt;a target=&quot;_blank&quot; href=&quot;https://antonin.delpeuch.eu/posts/too-much-time-on-your-hands/&quot;&gt;cette article&lt;/a&gt; et je n&amp;rsquo;arrête pas d&amp;rsquo;y penser. Je le met en perspective des contributeurs de GitRoot. Je suis le seul contributeur (à part mon ami Damien qui a corrigé de nombreuses fautes, merci à lui) sur ce projet. Et comme le montre ce &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge/statuses/01K98BCFEB9V430JD9TFQ32YZN&quot;&gt;toot&lt;/a&gt; j&amp;rsquo;ai un travail pour payer les factures, 1 femme et 2 enfants qui m&amp;rsquo;occupent pratiquement tout le reste du temps. À ça je rajoute GitRoot, pour le moment je m&amp;rsquo;éclate et je tiens bon mais pour combien de temps je vais y arriver ?&lt;/p&gt;

&lt;p&gt;Alors pourquoi pas de contributeurs externes sur GitRoot ?&lt;/p&gt;

&lt;p&gt;Je me dit souvent que le projet est jeune, trop jeune, pour que ça soit accessible à un developpeur externe. Parfois je me dit qu&amp;rsquo;un meilleurs système de ticket serait nécessaire pour avoir un label &amp;ldquo;welcome new contribution&amp;rdquo;. Parfois je me dit qu&amp;rsquo;une fois que j&amp;rsquo;aurais déployé les plugins sdk et fait un market place de plugins, enfin vous pourrez venir m&amp;rsquo;aider à faire que GitRoot soit un vrai projet open-source.&lt;/p&gt;

&lt;p&gt;Mais, car il y a un mais, cette liste ne s&amp;rsquo;arrêtera jamais. Soyons franc il y aura toujours un truc de plus à faire avant que vous puissiez m&amp;rsquo;aider à faire que GitRoot soit au top. J&amp;rsquo;ai donc décidé de faire la chose la plus simple à l&amp;rsquo;heure actuelle pour vous expliquer comment m&amp;rsquo;aider: un article de blog.&lt;/p&gt;

&lt;p&gt;Ici, je vais vous expliquer tout ce qu&amp;rsquo;il est possible de faire en l&amp;rsquo;état actuel du projet. Pas ce qu&amp;rsquo;il sera possible de faire un jour &lt;em&gt;peu être&lt;/em&gt;, mais ce que vous pouvez faire là maintenant. Je réitèrerais ce genre d&amp;rsquo;article tant que nécessaire.&lt;/p&gt;

&lt;h2 id=&quot;pré-requis&quot;&gt;Pré-requis&lt;/h2&gt;

&lt;p&gt;Il n&amp;rsquo;y a pas grand chose à faire pour être prêt à aider sur GitRoot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;vous devez avoir &lt;a target=&quot;_blank&quot; href=&quot;https://git-scm.com/&quot;&gt;git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;git clone ssh://gitroot.dev&lt;/li&gt;
&lt;li&gt;cd gitroot.dev&lt;/li&gt;
&lt;li&gt;./gitroot/init.sh&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si vous souhaitez coder il vous faudra en plus:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/bash/manual/bash.html&quot;&gt;bash&lt;/a&gt; et &lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/make/&quot;&gt;make&lt;/a&gt; (si vous êtes dev il y a de grandes chances que vous les aillez déjà)&lt;/li&gt;
&lt;li&gt;installer &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt;, c&amp;rsquo;est le plus simple pour avoir tous les compilateurs dans les bonnes versions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Et voila vous êtes bon pour commencer à aider. Mais que faire ? Et bien comme tout bon developpeur je vais vous répondre &amp;ldquo;ça dépend&amp;rdquo;. Ça dépend de ce que vous voulez faire ! Je vais lister dessous un ensemble de taches possibles, vous choisissez.&lt;/p&gt;

&lt;h2 id=&quot;avant-de-commencer&quot;&gt;Avant de commencer&lt;/h2&gt;

&lt;p&gt;Avant de vous dire ce que vous pouvez faire, je me dit qu&amp;rsquo;il serait bien de vous expliquer ce que moi je fais. Donc pour cette version 0.3.0 ma priorité absolue est de faire en sorte que GitRoot build GitRoot lui même. Aujourd&amp;rsquo;hui à chaque release je fais les binaires sur ma macine et je les scp. Je veux qu&amp;rsquo;avec cette version 0.3.0 cela ne soit plus le cas.&lt;/p&gt;

&lt;p&gt;Donc, si j&amp;rsquo;arrive à me retenir, je ne devrais pas faire les tickets qui suivent. Vous avez tout le temps dont vous souhaitez pour les faire. Le plus simple est, je pense, de nous retrouver sur &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;matrix channel gitroot&lt;/a&gt; pour en discutter.&lt;/p&gt;

&lt;p&gt;On peut également en parler par mail &lt;a href=&quot;blog/contact@gitroot.dev&quot;&gt;contact@gitroot.dev&lt;/a&gt; ou sur le fediverse &lt;cite class=&quot;informative&quot;&gt;&lt;a href=&quot;#forge@gitrrot.dev&quot;&gt;&lt;sup&gt;[forge@gitrrot.dev]&lt;/sup&gt;&lt;/a&gt;&lt;/cite&gt;(&lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;https://gts.gitroot.dev/@forge&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;qu-est-ce-qu-on-peut-faire&quot;&gt;Qu&amp;rsquo;est-ce qu&amp;rsquo;on peut faire ?&lt;/h2&gt;

&lt;h3 id=&quot;corriger-les-fautes&quot;&gt;Corriger les fautes&lt;/h3&gt;

&lt;p&gt;L&amp;rsquo;anglais n&amp;rsquo;est pas ma langue d&amp;rsquo;origine et je pense qu&amp;rsquo;il y a beaucoup de fautes, donc une tache pourrais être de lire tous les md dans &lt;code&gt;/doc/&lt;/code&gt;, &lt;code&gt;/blog/&lt;/code&gt; et pourquoi pas &lt;code&gt;/issues/&lt;/code&gt;. Vous corrigez, faites une branche et poussez. Je m&amp;rsquo;occupe du reste !&lt;/p&gt;

&lt;h3 id=&quot;moi-je-veux-faire-du-web&quot;&gt;Moi je veux faire du web&lt;/h3&gt;

&lt;p&gt;Ce qui serait top, ce serait de créer (ou de trouver) un thème. Aujourd&amp;rsquo;hui GitRoot utilise &lt;a target=&quot;_blank&quot; href=&quot;https://simplecss.org/&quot;&gt;simplecss&lt;/a&gt;, et il n&amp;rsquo;a besoin que de &lt;code&gt;css classless&lt;/code&gt;, il en existe beaucoup mais vous pouvez aussi créer celui de GitRoot.&lt;/p&gt;

&lt;p&gt;Le plus simple pour essayer est de lancer la commande &lt;code&gt;make testsuite&lt;/code&gt; quand elle a fini rendez vous dans &lt;code&gt;cd /tmp/repo1&lt;/code&gt; éditez le fichier &lt;code&gt;style.css&lt;/code&gt; puis &lt;code&gt;git add .&lt;/code&gt; &lt;code&gt;git commit -m &amp;quot;try style&amp;quot;&lt;/code&gt; et &lt;code&gt;git push&lt;/code&gt;, ouvrez &lt;a target=&quot;_blank&quot; href=&quot;http://127.0.0.1:4546/repo1&quot;&gt;http://127.0.0.1:4546/repo1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Quand vous êtes content faites une branche avec votre css dans &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/resources/styles/&quot;&gt;apex&lt;/a&gt;, puis &lt;code&gt;git add .&lt;/code&gt; et &lt;code&gt;git push&lt;/code&gt; je m&amp;rsquo;occupe du reste.&lt;/p&gt;

&lt;h3 id=&quot;moi-je-veux-coder&quot;&gt;Moi je veux coder&lt;/h3&gt;

&lt;p&gt;Bien, pas de soucis, voici une liste de petites tâches (enfin elles me paraissent petites mais si c&amp;rsquo;est pas le cas pour vous dites le moi):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;en golang:

&lt;ul&gt;
&lt;li&gt;dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/grafter/&quot;&gt;grafter&lt;/a&gt; pouvoir afficher &lt;a href=&quot;issues/3d33-grafter-dco.html&quot;&gt;autre chose&lt;/a&gt; dans le 1er message d&amp;rsquo;un graft. La bannière est en dure dans le plugin, il faudrait l&amp;rsquo;exporter dans la conf du plugin et l&amp;rsquo;utiliser.&lt;/li&gt;
&lt;li&gt;dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/silo/&quot;&gt;silo&lt;/a&gt; permettre de &lt;a href=&quot;issues/76ec-silo-more-selects.html&quot;&gt;grouper par fichier ou date&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;plus difficile mais j&amp;rsquo;aimerais bien avoir plus de &lt;a href=&quot;issues/e6d8-apex-alert-support.html&quot;&gt;renderer&lt;/a&gt; dans &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/&quot;&gt;apex&lt;/a&gt;, par exemple les infos, warning et autre&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;en typescript:

&lt;ul&gt;
&lt;li&gt;dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; il y a un bug sur le rss &lt;a href=&quot;issues/3986-pollen-rss-grow-infinite.html&quot;&gt;il est jamais nettoyé&lt;/a&gt;. Rajouté une option (ou pas, 100 en dur me semble être déjà pas mal) pour limiter le nombre d&amp;rsquo;&lt;code&gt;&amp;lt;item&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;toujours dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; pourquoi pas rajouter un &lt;a target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/JSON_Feed&quot;&gt;json feed&lt;/a&gt; en plus du rss. On peut serializer du json en assemblescript.&lt;/li&gt;
&lt;li&gt;plus difficile mais il serait bien de pouvoir dire au plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; de générer un rss avec le contenu des fichiers. Ça permettrait à GitRoot d&amp;rsquo;avoir un rss affichant les articles du blog par exemple. Aujourd&amp;rsquo;hui il ne met dans le rss que les commits.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;en rust:

&lt;ul&gt;
&lt;li&gt;faire ce que Ygor à dit dans &lt;a target=&quot;_blank&quot; href=&quot;https://floss.social/@ygor/115474535138702961&quot;&gt;https://floss.social/@ygor/115474535138702961&lt;/a&gt; je ne suis pas un developpeur rust et transformer des &lt;code&gt;String&lt;/code&gt; en &lt;code&gt;&amp;amp;str&lt;/code&gt; me parle pas beaucoup mais si vous l&amp;rsquo;êtes je suis sûr que c&amp;rsquo;est une petite tâche. Le code se trouve dans le &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; et le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/hop/&quot;&gt;hop&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;ajouter un &lt;a href=&quot;issues/9f00-rust-sdk-fake-server-test.html&quot;&gt;fakeServer&lt;/a&gt; au &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; pour faciliter les tests d&amp;rsquo;un plugin, voici celui de go &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&quot;&gt;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;moi-je-suis-sysadmin&quot;&gt;Moi je suis sysadmin&lt;/h3&gt;

&lt;p&gt;Parfait il y a de quoi faire aussi:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;aujourd&amp;rsquo;hui il y a trop d&amp;rsquo;étapes pour releaser GitRoot, &lt;a href=&quot;issues/dba0-simplify-release.html&quot;&gt;automatiser&lt;/a&gt; au maximum m&amp;rsquo;aiderais beaucoup.&lt;/li&gt;
&lt;li&gt;si vous avez un petit de place sur un de vos serveurs, &lt;a href=&quot;doc/tutorials/deploy_instance.html&quot;&gt;tester de déployer une instance&lt;/a&gt; juste pour le fun. Peu être vous pourrez améliorer la doc ou trouver quelque chose qui ne marche plus. Ou ajouter un paragraphe pour utiliser gitroot avec nginx ou autre.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;moi-je-veux-tester&quot;&gt;Moi je veux tester&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;tous les tests sont en bash dans &lt;a href=&quot;app/testsuite/runtest.sh&quot;&gt;testsuite/runtest.sh&lt;/a&gt; se trouve le plus gros test, à côté il y en d&amp;rsquo;autres

&lt;ul&gt;
&lt;li&gt;exécutez les pour voir s&amp;rsquo;ils marchent chez vous me parait une bonne première tâche&lt;/li&gt;
&lt;li&gt;en créer de nouveaux&lt;/li&gt;
&lt;li&gt;améliorer les existants en sortant par exemple des fonctions helper comme &lt;code&gt;quiet_git()&lt;/code&gt;, &lt;code&gt;report()&lt;/code&gt; ou &lt;code&gt;mySleep()&lt;/code&gt; dans d&amp;rsquo;autres fichiers (et les importer) serait d&amp;rsquo;une grande aide pour le projet. Vous pouvez aussi créer des nouveaux pour &lt;code&gt;killGitroot()&lt;/code&gt; qui est fait en début de chaque fichier. Ou &lt;code&gt;createProject()&lt;/code&gt; qui consiste à chaque fois à aller dans le root repository et ajouter son nom dans le fichier &lt;code&gt;.gitroot/repositories.yml&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;moi-je-veux-juste-réfléchir-discutter&quot;&gt;Moi je veux juste réfléchir/discutter&lt;/h3&gt;

&lt;p&gt;Tu es le bienvenue sur le &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;channel gitroot&lt;/a&gt; dans matrix. Ou par mail &lt;code&gt;contact [AT] gitroot.dev&lt;/code&gt; ou sur le fediverse &lt;code&gt;@forge@gitroot.dev&lt;/code&gt; &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;sur ce serveur&lt;/a&gt;.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/how-to-help_fr_11-2025.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/how-to-help_en_11-2025.md</link><title>How to help building GitRoot</title><pubDate>2025/11/18</pubDate><description>&lt;h1 id=&quot;how-to-help-building-gitroot-11-2025&quot;&gt;How to help building GitRoot 11-2025&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;This article is the translation of &lt;a href=&quot;blog/how-to-help_fr_11-2025.html&quot;&gt;french version&lt;/a&gt; thanks to &lt;a target=&quot;_blank&quot; href=&quot;https://www.deepl.com&quot;&gt;DeepL&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I just read &lt;a target=&quot;_blank&quot; href=&quot;https://antonin.delpeuch.eu/posts/too-much-time-on-your-hands/&quot;&gt;this article&lt;/a&gt; and I can&amp;rsquo;t stop thinking about it. I&amp;rsquo;m putting it into perspective for GitRoot contributors. I&amp;rsquo;m the only contributor (apart from my friend Damien, who corrected numerous typos, thank you to him) on this project. And as this &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge/statuses/01K98BCFEB9V430JD9TFQ32YZN&quot;&gt;toot&lt;/a&gt; shows, I have a job to pay the bills, a wife and two children who take up practically all the rest of my time. On top of that, I have GitRoot. For now, I&amp;rsquo;m having a blast and hanging in there, but how long can I keep it up?&lt;/p&gt;

&lt;p&gt;So why not have external contributors on GitRoot?&lt;/p&gt;

&lt;p&gt;I often tell myself that the project is young, too young, to be accessible to an external developer. Sometimes I think that a better issues system would be necessary to have a “welcome new contribution” label. Sometimes I think that once I&amp;rsquo;ve deployed the SDK plugins and created a plugin marketplace, then you can come and help me make GitRoot a true open-source project.&lt;/p&gt;

&lt;p&gt;But, because there is a but, this list will never end. Let&amp;rsquo;s be honest, there will always be something else to do before you can help me make GitRoot the best it can be. So I decided to do the simplest thing possible right now to explain how you can help me: a blog post.&lt;/p&gt;

&lt;p&gt;Here, I&amp;rsquo;ll explain everything that can be done in the current state of the project. Not what might be possible someday, but what you can do right now. I&amp;rsquo;ll repeat this kind of article as often as necessary.&lt;/p&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;There isn&amp;rsquo;t much you need to do to get ready to help out on GitRoot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you need to have &lt;a target=&quot;_blank&quot; href=&quot;https://git-scm.com/&quot;&gt;git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;git clone ssh://gitroot.dev&lt;/li&gt;
&lt;li&gt;cd gitroot.dev&lt;/li&gt;
&lt;li&gt;./gitroot/init.sh&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to code, you will also need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/bash/manual/bash.html&quot;&gt;bash&lt;/a&gt; and &lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/make/&quot;&gt;make&lt;/a&gt; (if you are a developer, chances are you already have them)&lt;/li&gt;
&lt;li&gt;Install &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt;, which is the easiest way to get all the right versions of the compilers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And now you&amp;rsquo;re ready to start helping. But what should you do? Well, like any good developer, I&amp;rsquo;ll answer, &amp;ldquo;It depends.&amp;rdquo;. It depends on what you want to do! I&amp;rsquo;ll list a set of possible tasks below, and you can choose.&lt;/p&gt;

&lt;h2 id=&quot;before-we-begin&quot;&gt;Before we begin&lt;/h2&gt;

&lt;p&gt;Before telling you what you can do, I thought it would be a good idea to explain what I am doing. So, for this version 0.3.0, my top priority is to make sure that GitRoot builds GitRoot itself. Currently, for each release, I build the binaries on my machine and then scp them. I want this to no longer be the case with version 0.3.0.&lt;/p&gt;

&lt;p&gt;So, if I can restrain myself, I shouldn&amp;rsquo;t have to do the following tickets. You have all the time you need to do them. The easiest thing, I think, is to meet up on &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;matrix channel gitroot&lt;/a&gt; to discuss them.&lt;/p&gt;

&lt;p&gt;We can also talk about it by email &lt;a href=&quot;blog/contact@gitroot.dev&quot;&gt;contact@gitroot.dev&lt;/a&gt; or on the fediverse &lt;cite class=&quot;informative&quot;&gt;&lt;a href=&quot;#forge@gitrrot.dev&quot;&gt;&lt;sup&gt;[forge@gitrrot.dev]&lt;/sup&gt;&lt;/a&gt;&lt;/cite&gt;(&lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;https://gts.gitroot.dev/@forge&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;what-can-we-do&quot;&gt;What can we do?&lt;/h2&gt;

&lt;h3 id=&quot;correct-mistakes&quot;&gt;Correct mistakes&lt;/h3&gt;

&lt;p&gt;English is not my native language, and I think there are a lot of typos, so one task could be to read all the MD files in &lt;code&gt;/doc/&lt;/code&gt;, &lt;code&gt;/blog/&lt;/code&gt;, and maybe even &lt;code&gt;/issues/&lt;/code&gt;. Correct them, create a branch, and push. I&amp;rsquo;ll take care of the rest!&lt;/p&gt;

&lt;h3 id=&quot;i-want-to-work-on-the-web&quot;&gt;I want to work on the web&lt;/h3&gt;

&lt;p&gt;It would be great to create (or find) a theme. GitRoot currently uses &lt;a target=&quot;_blank&quot; href=&quot;https://simplecss.org/&quot;&gt;simplecss&lt;/a&gt;, and it only needs &lt;code&gt;classless CSS&lt;/code&gt;. There are many available, but you can also create one for GitRoot.&lt;/p&gt;

&lt;p&gt;The easiest way to try it out is to run the &lt;code&gt;make testsuite&lt;/code&gt; command. When it&amp;rsquo;s finished, go to &lt;code&gt;cd /tmp/repo1&lt;/code&gt;, edit the &lt;code&gt;style.css&lt;/code&gt; file, then &lt;code&gt;git add .&lt;/code&gt; &lt;code&gt;git commit -m &amp;quot;try style&amp;quot;&lt;/code&gt; and &lt;code&gt;git push&lt;/code&gt;. Open &lt;a target=&quot;_blank&quot; href=&quot;http://127.0.0.1:4546/repo1&quot;&gt;http://127.0.0.1:4546/repo1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When you are satisfied, create a branch with your CSS in &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/resources/styles/&quot;&gt;apex&lt;/a&gt;, then &lt;code&gt;git add .&lt;/code&gt; and &lt;code&gt;git push&lt;/code&gt;. I&amp;rsquo;ll take care of the rest.&lt;/p&gt;

&lt;h3 id=&quot;i-want-to-code&quot;&gt;I want to code&lt;/h3&gt;

&lt;p&gt;Okay, no worries, here&amp;rsquo;s a list of small tasks (well, they seem small to me, but if that&amp;rsquo;s not the case for you, let me know):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;in golang:

&lt;ul&gt;
&lt;li&gt;in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/grafter/&quot;&gt;grafter&lt;/a&gt; plugin, be able to display &lt;a href=&quot;issues/3d33-grafter-dco.html&quot;&gt;something else&lt;/a&gt; in the first message of a graft. The banner is hard-coded in the plugin; it would need to be exported to the plugin configuration and used.&lt;/li&gt;
&lt;li&gt;In the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/silo/&quot;&gt;silo&lt;/a&gt; plugin, allow &lt;a href=&quot;issues/76ec-silo-more-selects.html&quot;&gt;grouping by file or date&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;More difficult, but I would like to have more &lt;a href=&quot;issues/e6d8-apex-alert-support.html&quot;&gt;renderers&lt;/a&gt; in &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/&quot;&gt;apex&lt;/a&gt;, for example info, warnings, and others&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;in typescript:

&lt;ul&gt;
&lt;li&gt;in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; plugin there is a bug in the RSS feed &lt;a href=&quot;issues/3986-pollen-rss-grow-infinite.html&quot;&gt;it is never cleaned up&lt;/a&gt;. Added an option (or not, 100 seems fine to me) to limit the number of &lt;code&gt;&amp;lt;item&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Still in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; plugin, why not add a &lt;a target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/JSON_Feed&quot;&gt;json feed&lt;/a&gt; in addition to the RSS feed? We can serialize json in assemblescript.&lt;/li&gt;
&lt;li&gt;More difficult, but it would be nice to be able to tell the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; plugin to generate an RSS feed with the contents of the files. This would allow GitRoot to have an RSS feed displaying blog articles, for example. Currently, it only puts commits in the RSS feed.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;in Rust:

&lt;ul&gt;
&lt;li&gt;Do what Ygor said in &lt;a target=&quot;_blank&quot; href=&quot;https://floss.social/@ygor/115474535138702961&quot;&gt;https://floss.social/@ygor/115474535138702961&lt;/a&gt; I&amp;rsquo;m not a Rust developer and converting &lt;code&gt;String&lt;/code&gt; to &lt;code&gt;&amp;amp;str&lt;/code&gt; doesn&amp;rsquo;t mean much to me, but if you are, I&amp;rsquo;m sure it&amp;rsquo;s a small task. The code can be found in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; and the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/hop/&quot;&gt;hop&lt;/a&gt; plugin.&lt;/li&gt;
&lt;li&gt;Add a &lt;a href=&quot; issues/9f00-rust-sdk-fake-server-test.html&quot;&gt;fakeServer&lt;/a&gt; to the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; to facilitate plugin testing. Here is the one for Go &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&quot;&gt;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;i-m-a-sysadmin&quot;&gt;I&amp;rsquo;m a sysadmin.&lt;/h3&gt;

&lt;p&gt;Perfect, there&amp;rsquo;s plenty to do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Today, there are too many steps to release GitRoot. &lt;a href=&quot;issues/dba0-simplify-release.html&quot;&gt;Automating&lt;/a&gt; as much as possible would help me a lot.&lt;/li&gt;
&lt;li&gt;If you have a little space on one of your servers, &lt;a href=&quot;doc/tutorials/deploy_instance.html&quot;&gt;try deploying an instance&lt;/a&gt; just for fun. Maybe you can improve the documentation or find something that no longer works. Or add a paragraph on using gitroot with nginx or something else.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;i-want-to-test&quot;&gt;I want to test&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;All tests are in bash in &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/testsuite/&quot;&gt;/app/testsuite/&lt;/a&gt;. The largest test is located in &lt;code&gt;runtest.sh&lt;/code&gt;, along with others.

&lt;ul&gt;
&lt;li&gt;Running them to see if they work on your system seems like a good first task to me.&lt;/li&gt;
&lt;li&gt;Creating new ones&lt;/li&gt;
&lt;li&gt;Improving existing ones by, for example, moving helper functions such as &lt;code&gt;quiet_git()&lt;/code&gt;, &lt;code&gt;report()&lt;/code&gt;, or &lt;code&gt;mySleep()&lt;/code&gt; to other files (and importing them) would be a great help to the project. You can also create new ones for &lt;code&gt;killGitroot()&lt;/code&gt;, which is done at the beginning of each file. Or &lt;code&gt;createProject()&lt;/code&gt;, which consists of going to the root repository each time and adding its name to the &lt;code&gt;.gitroot/repositories.yml&lt;/code&gt; file&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;i-just-want-to-think-discuss&quot;&gt;I just want to think/discuss&lt;/h3&gt;

&lt;p&gt;You are welcome to join the &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;gitroot channel&lt;/a&gt; on Matrix. Or contact us by email at &lt;code&gt;contact [AT] gitroot.dev&lt;/code&gt; or on the Fediverse at &lt;code&gt;@forge@gitroot.dev&lt;/code&gt; &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;on this server&lt;/a&gt;.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/how-to-help_en_11-2025.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/issues.md</link><title>Issues, Tickets, Discussions... Call them what you want!</title><pubDate>2026/06/08</pubDate><description>&lt;h1 id=&quot;issues-tickets-discussions-call-them-what-you-want&quot;&gt;Issues, Tickets, Discussions&amp;hellip; Call them what you want!&lt;/h1&gt;

&lt;p&gt;Many of you, when checking out &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev&quot;&gt;https://gitroot.dev&lt;/a&gt;, have told me that the &lt;code&gt;🐞 Issues&lt;/code&gt; button shouldn&amp;rsquo;t be labeled &amp;ldquo;Issues&amp;rdquo;, but rather &amp;ldquo;Tickets&amp;rdquo; or &amp;ldquo;Discussions&amp;rdquo;.&lt;/p&gt;

&lt;p&gt;First of all, thanks for the feedback! I really appreciate it, that&amp;rsquo;s how I can build a better forge. Of course, I will change the label, but developing a forge means having a lot of different areas to work on: issues/boards, git, code, CI/CD, and with GitRoot, even more, like plugins and WASM&amp;hellip;&lt;/p&gt;

&lt;p&gt;So, why don&amp;rsquo;t I just change this label right away? In reality, this label isn&amp;rsquo;t hardcoded inside GitRoot. It&amp;rsquo;s only there because I configured plugins to display it. I want to take this opportunity to explain how GitRoot works, because the platform is developed around this exact concept: how to customize a forge at the project level.&lt;/p&gt;

&lt;h2 id=&quot;the-original-idea&quot;&gt;The Original Idea&lt;/h2&gt;

&lt;p&gt;Imagine you are developing a forge, and to avoid vendor lock-in, you decide not to put anything outside of Git. You choose not to use any database at all. In this context, how do you design an &amp;ldquo;issue&amp;rdquo; system?&lt;/p&gt;

&lt;p&gt;In traditional forges, an &amp;ldquo;issue&amp;rdquo; consists of a title and a description created by a user. We can represent it like this:&lt;/p&gt;
&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;379.68002&quot; height=&quot;332.2&quot; viewBox=&quot;0 0 379.68002 332.2&quot;&gt;&lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;379.68002&quot; height=&quot;332.2&quot; fill=&quot;transparent&quot;/&gt;&lt;defs&gt;&lt;marker id=&quot;arrow-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 10 5 L 0 10 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-start-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;4.5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 5 L 10 10 L 10 0 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-open-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;1&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1 7 L 18 13 V 1 Z&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-open-start-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;18&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1 7 L 18 13 V 1 Z&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-dep-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;13&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 18 7 L 9 13 L 14 7 L 9 1 Z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-dep-start-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;6&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 5 7 L 9 13 L 1 7 L 9 1 Z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;path id=&quot;edge-0&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-0&quot; d=&quot;M 135.960,121.250 L 96.540,175.250&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot; x=&quot;85.37&quot; y=&quot;134.25&quot; width=&quot;61.76&quot; height=&quot;28.00&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.85&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.35&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;116.25&quot; y=&quot;151.75&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;116.25&quot; dy=&quot;0.00&quot;&gt;Author&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-1&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-1&quot; d=&quot;M 231.960,121.250 L 255.380,175.250&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-1&quot; data-label-kind=&quot;center&quot; x=&quot;217.27&quot; y=&quot;134.25&quot; width=&quot;52.80&quot; height=&quot;28.00&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.85&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.35&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-1&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;243.67&quot; y=&quot;151.75&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;243.67&quot; dy=&quot;0.00&quot;&gt;State&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;rect x=&quot;87.90&quot; y=&quot;8.00&quot; width=&quot;192.12&quot; height=&quot;113.25&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;line x1=&quot;93.90&quot; y1=&quot;46.95&quot; x2=&quot;274.02&quot; y2=&quot;46.95&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1.0&quot;/&gt;&lt;text x=&quot;183.96&quot; y=&quot;34.00&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;183.96&quot; dy=&quot;0.00&quot; font-weight=&quot;600&quot;&gt;Issue&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;117.90&quot; y=&quot;69.70&quot; text-anchor=&quot;start&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;117.90&quot; dy=&quot;0.00&quot;&gt;int id&lt;/tspan&gt;&lt;tspan x=&quot;117.90&quot; dy=&quot;17.85&quot;&gt;string title&lt;/tspan&gt;&lt;tspan x=&quot;117.90&quot; dy=&quot;17.85&quot;&gt;string description&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;203.08&quot; y=&quot;175.25&quot; width=&quot;168.60&quot; height=&quot;148.95&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;line x1=&quot;209.08&quot; y1=&quot;214.20&quot; x2=&quot;365.68&quot; y2=&quot;214.20&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1.0&quot;/&gt;&lt;text x=&quot;287.38&quot; y=&quot;201.25&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;287.38&quot; dy=&quot;0.00&quot; font-weight=&quot;600&quot;&gt;Status&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;233.08&quot; y=&quot;236.95&quot; text-anchor=&quot;start&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;0.00&quot;&gt;&amp;lt;&amp;lt;enumeration&amp;gt;&amp;gt;&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;Open&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;TODO&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;DOING&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;CLOSE&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;8.00&quot; y=&quot;175.25&quot; width=&quot;145.08&quot; height=&quot;95.40&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;line x1=&quot;14.00&quot; y1=&quot;214.20&quot; x2=&quot;147.08&quot; y2=&quot;214.20&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1.0&quot;/&gt;&lt;text x=&quot;80.54&quot; y=&quot;201.25&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;80.54&quot; dy=&quot;0.00&quot; font-weight=&quot;600&quot;&gt;User&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;38.00&quot; y=&quot;236.95&quot; text-anchor=&quot;start&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;38.00&quot; dy=&quot;0.00&quot;&gt;int id&lt;/tspan&gt;&lt;tspan x=&quot;38.00&quot; dy=&quot;17.85&quot;&gt;string email&lt;/tspan&gt;&lt;/text&gt;&lt;/svg&gt;
&lt;p&gt;Easy, right? Well, how do you represent that in GitRoot using only files? This question was running through my mind for months before I finally found a solution.&lt;/p&gt;

&lt;h2 id=&quot;the-basic-solution&quot;&gt;The Basic Solution&lt;/h2&gt;

&lt;p&gt;Since I only have files at my disposal, what actually constitutes an &amp;ldquo;issue&amp;rdquo;? I need to add some metadata to a file so the system understands that this file is an issue and not, say, a &lt;code&gt;main.go&lt;/code&gt;. I’ll probably want to group all these &amp;ldquo;issues&amp;rdquo; into a specific directory, because while GitRoot technically lets you do whatever you want (even dropping an issue right in the middle of your source code), I personally prefer to keep my files organized!&lt;/p&gt;

&lt;p&gt;A simple text format that handles metadata perfectly is Markdown. I use Markdown here, but any other markup language could work. Let&amp;rsquo;s create a file named &lt;code&gt;issues/1.md&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;id: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;user: email@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;status: OPEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# My title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt;9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;My description where I can detail what my problem is.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yes, it really is that simple. In GitRoot, to create an issue, you just create a file, add some metadata, commit, and push. Boom, you have an issue.&lt;/p&gt;

&lt;p&gt;Now, you might be thinking: &amp;ldquo;Well, I lose a lot of control with that, the status could be anything!&amp;rdquo; or &amp;ldquo;How are other users supposed to know what to put where?&amp;rdquo;. You&amp;rsquo;re right, and that is exactly where plugins enter the game.&lt;/p&gt;

&lt;h2 id=&quot;a-better-solution&quot;&gt;A Better Solution&lt;/h2&gt;

&lt;p&gt;If I simplify this as much as possible, what does an external contributor actually need to provide to create an issue? Just a title and a description. So, let&amp;rsquo;s create a file named &lt;code&gt;issues/my_issue.md&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# My title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;My description where I can detail what my problem is.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we run &lt;code&gt;git commit &amp;amp;&amp;amp; git push&lt;/code&gt;. Since I have configured the &lt;a href=&quot;plugins/name/ladybug.html&quot;&gt;ladybug plugin&lt;/a&gt; like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: ladybug
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: issues/**/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;func&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: issues/**/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;callfunc&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;default&lt;/span&gt;: autogenerated
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;mandatory&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: id
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;type&lt;/span&gt;: crc16
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;default&lt;/span&gt;: OPEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;mandatory&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: status
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;type&lt;/span&gt;: string
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;default&lt;/span&gt;: email@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;mandatory&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: user
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;type&lt;/span&gt;: string
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can read this configuration as: &amp;ldquo;For every Markdown file in the &lt;code&gt;issues&lt;/code&gt; directory across all branches, if the file doesn&amp;rsquo;t have these metadata fields, automatically add these default values.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, if you are a Git purist, a server creating commits right on top of yours might sound a bit radical, or even terrifying. But look closely at the &lt;code&gt;write&lt;/code&gt; block in the YAML configuration above:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: issues/**/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;GitRoot doesn&amp;rsquo;t just let plugins run wild, the project configuration must explicitly grant the plugin permission to write or modify files on specific paths. If you don&amp;rsquo;t declare these rights, GitRoot will block the plugin from making any changes.&lt;/p&gt;

&lt;p&gt;When granted, GitRoot intercepts your push, lets the plugin inject the missing metadata, and safely generates a follow-up commit. And don&amp;rsquo;t worry about your history: if you decide to &lt;code&gt;git rebase -i&lt;/code&gt; later to squash or fixup your commits, plugins won&amp;rsquo;t generate duplicate commits if no metadata has changed.&lt;/p&gt;

&lt;p&gt;What about security or external users trying to forge metadata? Since contributors cannot push directly to the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt; (like &lt;code&gt;main&lt;/code&gt;), they have to go through a branch and need to be merged. This means every single change to an issue&amp;rsquo;s status or metadata is subjected to a code review.&lt;/p&gt;

&lt;p&gt;If an external user tries to close someone else&amp;rsquo;s issue or manipulate IDs, it will stand out like a sore thumb in the git diff. Maintainers retain absolute control and can simply reject the rogue contribution. Security doesn&amp;rsquo;t rely on complex database permission tables, but on the battle-tested git review workflow you already know and trust.&lt;/p&gt;

&lt;p&gt;So, once you push, the server-side plugin does its job, and the next time you run &lt;code&gt;git pull&lt;/code&gt;, your local file will automatically look like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;id: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;user: email@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;status: OPEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# My title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt;9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;My description where I can detail what my problem is.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bingo! You get the exact same file, with the exact same metadata you wanted. Of course, a lot more needs to be added, like defining a &lt;code&gt;type: user&lt;/code&gt; to assign an issue, or perhaps a &lt;code&gt;type: status&lt;/code&gt; to define an enum of allowed values. But I think you see where I&amp;rsquo;m going with this feature!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: If you&amp;rsquo;re wondering about the barrier to entry for non-technical contributors, my long-term vision is to allow plugins to intercept standard HTTP requests. Tomorrow, a classic web form will be able to POST to GitRoot and automatically commit a &lt;code&gt;.md&lt;/code&gt; file under the hood. But for now, we are proudly dev-centric!)&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;so-what-about-the-label&quot;&gt;So, What About the Label?&lt;/h2&gt;

&lt;p&gt;Throughout all of this, you’ll notice that the term &amp;ldquo;issue&amp;rdquo; only appears within the plugins themselves. Nowhere have we talked about that &lt;code&gt;🐞 Issues&lt;/code&gt; button yet. We actually need to introduce another plugin before we can tackle that topic.&lt;/p&gt;

&lt;p&gt;First, we need a file that lists all of these &amp;ldquo;issues&amp;rdquo;. That’s exactly why the &lt;a href=&quot;plugins/name/silo.html&quot;&gt;silo plugin&lt;/a&gt; exists. It inspects all files and groups them whenever it finds a matching pattern.&lt;/p&gt;

&lt;p&gt;To list all open &amp;ldquo;issues&amp;rdquo;, we configure it like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: silo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;func&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: boards/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - append
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;callfunc&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;boards&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;description&lt;/span&gt;: All open issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;for&lt;/span&gt;: issues/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;format&lt;/span&gt;: table
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;paginator&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;50&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;selects&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;status: (.*)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;user: (.*)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;sort&lt;/span&gt;: select[0]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;sortOrder&lt;/span&gt;: desc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;tableHeader&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;| | status | user |&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;title&lt;/span&gt;: All issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;to&lt;/span&gt;: boards/issues.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;where&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;status: OPEN&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can read this configuration as: &amp;ldquo;For every file in the main branch, create a board called &amp;lsquo;All issues&amp;rsquo; containing links to all files matching &lt;code&gt;status: OPEN&lt;/code&gt;. Display them as a table with &lt;code&gt;status&lt;/code&gt; and &lt;code&gt;user&lt;/code&gt; columns.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After running &lt;code&gt;git commit &amp;amp;&amp;amp; git push &amp;amp;&amp;amp; git pull&lt;/code&gt;, you will get a new file at &lt;code&gt;boards/issues.md&lt;/code&gt; containing something like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# All issues
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;All open issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;|                                   | status | user            |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;| --------------------------------- | ------ | --------------- |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;| [&lt;span style=&quot;color:#ff79c6&quot;&gt;My title&lt;/span&gt;](&lt;span style=&quot;color:#50fa7b&quot;&gt;../issues/my_issue.md&lt;/span&gt;) | OPEN   | email@email.com |
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Silo has aggregated all your open issues into a single dashboard. Of course, you could easily create another board for closed issues in a separate file like &lt;code&gt;boards/closed.md&lt;/code&gt;, it&amp;rsquo;s entirely up to you.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: If you are sweating at the thought of a plugin scanning your entire repository on every single push, don&amp;rsquo;t panic. &lt;code&gt;silo&lt;/code&gt;, thanks to the plugin-sdk API, doesn&amp;rsquo;t do a full disk scan. It only processes the incremental diffs generated by the incoming commit. This is precisely why we restrict its scope to the &lt;code&gt;main&lt;/code&gt; branch in this example.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now that we have a page representing all our issues, we can finally render it in the browser. Because, as you might have guessed, the label causing all this debate lives in the Web UI.&lt;/p&gt;

&lt;h2 id=&quot;the-web-is-where-it-happens&quot;&gt;The Web is Where It Happens&lt;/h2&gt;

&lt;p&gt;To render my &lt;code&gt;boards/issues.md&lt;/code&gt; file on the web, I use the &lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex&lt;/a&gt; plugin, in conjunction with the &lt;a href=&quot;plugins/name/apex_markdown.html&quot;&gt;apex_markdown&lt;/a&gt; plugin, of course.&lt;/p&gt;

&lt;p&gt;These plugins take files from git and render them directly in the Web UI. I configure them like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: apex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;func&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - append
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - append
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;callfunc&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;pluginname&lt;/span&gt;: apex_markdown
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;funcname&lt;/span&gt;: renderMd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;menu&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 🏠 Home
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 📖 Documentation
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L38&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L38&quot;&gt;38&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /doc/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L39&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L39&quot;&gt;39&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 🔖 Versions
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L40&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L40&quot;&gt;40&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /CHANGELOG.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L41&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L41&quot;&gt;41&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: �� Plugins
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L42&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L42&quot;&gt;42&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /plugins/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L43&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L43&quot;&gt;43&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: �� Issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L44&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L44&quot;&gt;44&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /boards/issues.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L45&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L45&quot;&gt;45&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 🚀 Code
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L46&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L46&quot;&gt;46&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /worktree/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L47&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L47&quot;&gt;47&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: ☕ Blog
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L48&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L48&quot;&gt;48&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /blog/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L49&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L49&quot;&gt;49&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: �� Contact
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L50&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L50&quot;&gt;50&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /contact.html
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oh, do you see it now? Yes, right there: &lt;code&gt;display: 🐞 Issues&lt;/code&gt;. This is the exact configuration that tells Apex to add a global menu item with the &lt;code&gt;🐞 Issues&lt;/code&gt; label, pointing to the &lt;code&gt;/boards/issues.html&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;If you are wondering how Apex magically knows where to find your &amp;ldquo;issues&amp;rdquo;, the secret is: it doesn&amp;rsquo;t.&lt;/p&gt;

&lt;p&gt;Apex isn&amp;rsquo;t a smart router, it&amp;rsquo;s just a file translator. It blindly takes the Markdown structure generated by GitRoot and renders it into HTML. Because I know Apex transforms &lt;code&gt;boards/issues.md&lt;/code&gt; into &lt;code&gt;boards/issues.html&lt;/code&gt;, I simply hardcode that target URL in my menu configuration (&lt;code&gt;link: /boards/issues.html&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;And what about the links inside the table? Apex has a clever built-in rewrite engine. When it parses your generated board file, it automatically rewrites every internal Markdown link, turning &lt;code&gt;[My title](../issues/my_issue.md)&lt;/code&gt; into a standard web-friendly &lt;code&gt;&amp;lt;a href=&amp;quot;../issues/my_issue.html&amp;quot;&amp;gt;&lt;/code&gt;. No database, no dynamic routing, just plain old static files mapped one-to-one.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;If you change the label to &lt;code&gt;🐞 Tickets&lt;/code&gt; or &lt;code&gt;💡 Discussions&lt;/code&gt;, the &amp;ldquo;issue to rename issues into something else&amp;rdquo; can finally be closed. Ideally, you might also want to rename some directories in the other plugins&amp;rsquo; configurations, but it&amp;rsquo;s really not a big deal.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;m pretty sure you now understand why I didn&amp;rsquo;t immediately jump on resolving this task: everything we just saw is pure configuration, completely separated from the GitRoot core code.&lt;/p&gt;

&lt;p&gt;And to be perfectly honest, I haven&amp;rsquo;t changed it yet because none of the traditional options really appeal to me. I don&amp;rsquo;t particularly like &amp;ldquo;Tickets&amp;rdquo; or &amp;ldquo;Discussions&amp;rdquo;, maybe it should be &amp;ldquo;Feature Requests&amp;rdquo; instead (and yes, you can now model that in GitRoot too!).&lt;/p&gt;

&lt;p&gt;In fact, I personally call them &lt;code&gt;TODO&lt;/code&gt;s, because in reality, they are all just waiting for me to get them done.&lt;/p&gt;

&lt;h3 id=&quot;wait-why-stop-at-issues&quot;&gt;Wait&amp;hellip; Why stop at issues?&lt;/h3&gt;

&lt;p&gt;This is where the true power of GitRoot will blow your mind.&lt;/p&gt;

&lt;p&gt;Because GitRoot doesn&amp;rsquo;t care about databases, and because everything is just a file inside Git, the &lt;code&gt;silo&lt;/code&gt; plugin can aggregate anything. I don&amp;rsquo;t need a separate &lt;code&gt;TODO&lt;/code&gt; page for my tickets, because I already have one that automatically tracks the &lt;code&gt;//TODO&lt;/code&gt; comments inside my actual source code.&lt;/p&gt;

&lt;p&gt;Yes, you read that right. You can visit it live at &lt;a href=&quot;boards/todos.html&quot;&gt;https://gitroot.dev/boards/todos.html&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Every time I write a &lt;code&gt;//TODO&lt;/code&gt; in a &lt;code&gt;.go&lt;/code&gt;, &lt;code&gt;.rs&lt;/code&gt; or &lt;code&gt;.ts&lt;/code&gt; file, GitRoot parses it, aggregates it, and generates a dynamic dashboard. Tickets, documentation, source code comments&amp;hellip; it&amp;rsquo;s all the exact same data structure.&lt;/p&gt;

&lt;p&gt;To be clear, it doesn’t magically transform your source code comments into full-blown database-like issues with IDs and statuses. It simply extracts them to give you a centralized, living view of your technical debt, right alongside your official tickets. It’s the exact same data aggregation pipeline, tailored for two different use cases.&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to discover more plugins and configurations, feel free to browse through the &lt;a href=&quot;plugins&quot;&gt;🧩 Plugins section&lt;/a&gt;. However, for now, the best way to explore remains checking out the actual &lt;a href=&quot;.gitroot/plugins.yml&quot;&gt;GitRoot plugins.yml configuration file&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
</description><guid>https://gitroot.dev//blog/issues.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.5.0.md</link><title>0.5.0</title><pubDate>2026/10/05</pubDate><description>&lt;nav&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#move-slowly-and-take-care-of-things&quot;&gt;Move slowly, and take care of things&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#breaking-purl&quot;&gt;Breaking PURL&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#a-lot-of-people-have-discovered-gitroot&quot;&gt;A lot of people have discovered GitRoot&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#not-conclusive&quot;&gt;Not conclusive&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#before-it-s-too-late&quot;&gt;Before it&amp;rsquo;s too late&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#my-new-favorite-language-until-the-next-one&quot;&gt;My new favorite language&amp;hellip;. until the next one&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#plugins-needs-more-power&quot;&gt;Plugins needs more power&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#sftp-to-save-the-web&quot;&gt;Sftp to save the web&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#how-many-bugs&quot;&gt;How many bugs?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#in-depth-details&quot;&gt;In-depth Details&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#purl-breaking-details&quot;&gt;PURL breaking details&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#gitroot-communication&quot;&gt;GitRoot communication&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#sdkgenerator-full-story&quot;&gt;SdkGenerator full story&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#max-anonymous-branches&quot;&gt;Max anonymous branches&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#zig-plugin-sdk&quot;&gt;Zig plugin-sdk&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#pluginsdk-improvements&quot;&gt;PluginSdk improvements&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#sftp-feature&quot;&gt;Sftp feature&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#some-interesting-bug-fixes&quot;&gt;Some interesting bug fixes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#what-next&quot;&gt;What next?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;/nav&gt;

&lt;h1 id=&quot;move-slowly-and-take-care-of-things&quot;&gt;Move slowly, and take care of things&lt;/h1&gt;

&lt;p&gt;These days, everything moves too fast. They push thousands of lines of AI generated code every hour, deploying it without even understanding how it works. But with GitRoot, I want to do the exact opposite. I want to take my time, savor every architectural choice, and thoroughly enjoy writing every line of code by hand. It&amp;rsquo;s a project that is deeply close to my heart: a space for freedom far away from the rat race of metrics. So yes, we move at our own pace: &lt;code&gt;slowly, and take care of things&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote style=&quot;border-color: #006ac7&quot;&gt;

&lt;p&gt;Thanks to &lt;a target=&quot;_blank&quot; href=&quot;https://pouet.chapril.org/@lonugem&quot;&gt;lonugem&lt;/a&gt; for the title idea&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes&lt;/h2&gt;

&lt;h3 id=&quot;breaking-purl&quot;&gt;Breaking PURL&lt;/h3&gt;

&lt;p&gt;I&amp;rsquo;m sorry to say that the first feature I want to talk about is a big breaking one. I know, I know, on the homepage of GitRoot I say &amp;ldquo;no headaches to upgrade my forge&amp;rdquo;&amp;hellip;. but this time you will&amp;hellip; If you are not happy, you can contact the support and get a refund for the previous year and a 100% discount for the next one :D&lt;/p&gt;

&lt;p&gt;Joking aside, you need to replace your plugin URL with a PURL one. &lt;a target=&quot;_blank&quot; href=&quot;https://ecma-international.org/publications-and-standards/standards/ecma-427/&quot;&gt;ECMA PURL&lt;/a&gt; is a specification to produce &amp;ldquo;a standardized URL-based syntax that uniquely identifies software packages&amp;rdquo;.&lt;/p&gt;

&lt;p&gt;This corresponds to the following change:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#f55&quot;&gt;- url: https://gitroot.dev/releases/0.4.0/ladybug-0.0.4.wasm
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#f55&quot;&gt;- checksum: sha256:7409837469ce89d5c9fd8dd48236764650f48523ff4d81295967154977a01635
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#f55&quot;&gt;- name: ladybug
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#f55&quot;&gt;- version: 0.0.4
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#f55&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#50fa7b;font-weight:bold&quot;&gt;+ purl: pkg:gitroot/gitroot/ladybug@0.0.5?checksum=sha256:afb22a26e7d489d72db0a2305d433a3c3ac2692959ba2412011c0d759b4c8518&amp;amp;registry=https:%2F%2Fgitroot.dev%2Freleases%2F0.5.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#50fa7b;font-weight:bold&quot;&gt;&lt;/span&gt;  active: true
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;GitRoot will download &lt;code&gt;$registry/$scope-$name-$version.wasm&lt;/code&gt;, so it will be &lt;code&gt;https://gitroot.dev/releases/0.5.0/gitroot-ladybug-0.0.5.wasm&lt;/code&gt; for the example. And then compare the downloaded WASM binary with the checksum provided in the PURL.&lt;/p&gt;
&lt;blockquote style=&quot;border-color: #be4700&quot;&gt;

&lt;p&gt;Look in the &lt;a href=&quot;plugins/index.html&quot;&gt;plugins registry&lt;/a&gt; to find the exact purl for each plugin.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To know why, how and more &lt;a href=&quot;blog/version-0.5.0.html#purl-breaking-details&quot;&gt;read purl details&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;a-lot-of-people-have-discovered-gitroot&quot;&gt;A lot of people have discovered GitRoot&lt;/h3&gt;

&lt;p&gt;First show, first lobste.rs/HN links, first video&amp;hellip; Yes, more and more people are talking about GitRoot. &lt;a href=&quot;blog/version-0.5.0.html#gitroot-communication&quot;&gt;Discover where&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;not-conclusive&quot;&gt;Not conclusive&lt;/h3&gt;

&lt;p&gt;Sometimes you spend more time on a subject than you thought. And sometimes, after all of this time spent, you don&amp;rsquo;t have what you wanted to have at the start&amp;hellip; That is the case for the &lt;code&gt;sdkgenerator&lt;/code&gt; experiment. It should &amp;ldquo;just&amp;rdquo; generate Rust, TS, Zig code from Golang&amp;hellip; &amp;ldquo;Just&amp;rdquo;, lol, I never finished it. Maybe someone wants help? &lt;a href=&quot;blog/version-0.5.0.html#sdkgenerator-full-story&quot;&gt;Read the full sdkgenerator story&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;before-it-s-too-late&quot;&gt;Before it&amp;rsquo;s too late&lt;/h3&gt;

&lt;p&gt;How to prevent spam with a forge without registration? This question was asked by NLnet 2 years ago (no, they don&amp;rsquo;t fund GitRoot, I was rejected). Two years later I have the answer, not perfect, but it&amp;rsquo;s a start. I added two new properties: one in &lt;code&gt;.gitroot/forgeConf.yml&lt;/code&gt; of your root repository, &lt;code&gt;NbMaxBranchesPerProjectBeforeRejectingAnonymous&lt;/code&gt;, which is the number of branches before rejecting anonymous pushes. And one in &lt;code&gt;.gitroot/repoConfiguration.yml&lt;/code&gt; of all your repositories, &lt;code&gt;NbMaxBranchesBeforeRejectingAnonymous&lt;/code&gt;, which serves the same purpose but for a single project. &lt;a href=&quot;blog/version-0.5.0.html#max-anonymous-branches&quot;&gt;Read why and see the full example&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;my-new-favorite-language-until-the-next-one&quot;&gt;My new favorite language&amp;hellip;. until the next one&lt;/h3&gt;

&lt;p&gt;After Golang, AssemblyScript, and Rust, the plugin-sdk is now accessible for the Zig language. I continue to add new languages and discover how they work. This one was especially refreshing in its implementation. &lt;a href=&quot;blog/version-0.5.0.html#zig-plugin-sdk&quot;&gt;Read more&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;plugins-needs-more-power&quot;&gt;Plugins needs more power&lt;/h3&gt;

&lt;p&gt;Talking about the plugin-sdk, they all receive a lot of new features. They can now &lt;code&gt;schedule&lt;/code&gt; jobs, make &lt;code&gt;http&lt;/code&gt; requests, and configure plugins during &lt;code&gt;CallFunc&lt;/code&gt;. I have also simplified the initialization part. &lt;a href=&quot;blog/version-0.5.0.html#pluginsdk-improvements&quot;&gt;Read details&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;sftp-to-save-the-web&quot;&gt;Sftp to save the web&lt;/h3&gt;

&lt;p&gt;How to add images to the web without cluttering Git? That was the problem I had to tackle before adding slideshows to GitRoot. SFTP came to the rescue, and with ~200 lines of code, it&amp;rsquo;s now possible. I love when a simple, established protocol allows you to fix a complicated feature. &lt;a href=&quot;blog/version-0.5.0.html#sftp-feature&quot;&gt;Read more&lt;/a&gt; to know how to enable it.&lt;/p&gt;

&lt;h3 id=&quot;how-many-bugs&quot;&gt;How many bugs?&lt;/h3&gt;

&lt;p&gt;A lot of bug fixes have been done. Let me explain &lt;a href=&quot;blog/version-0.5.0.html#some-interesting-bug-fixes&quot;&gt;some interesting ones&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;in-depth-details&quot;&gt;In-depth Details&lt;/h2&gt;

&lt;h3 id=&quot;purl-breaking-details&quot;&gt;PURL breaking details&lt;/h3&gt;

&lt;p&gt;At the beginning of this story, I only wanted to add a &lt;a href=&quot;issues/close/5dd3-plugin-scope.html&quot;&gt;scope&lt;/a&gt; for plugins. That way, if you install a plugin with &lt;code&gt;gitroot&lt;/code&gt; as the scope, you can think &amp;ldquo;it looks less dangerous&amp;rdquo; than &lt;code&gt;anyother&lt;/code&gt; scope. Just like I suspect less &lt;code&gt;@angular&lt;/code&gt; than some &lt;code&gt;odd&lt;/code&gt; Node.js package (well, the npm ecosystem is maybe a bad example for that&amp;hellip; but the only one right now).&lt;/p&gt;

&lt;p&gt;So, my simple idea was to transform &lt;code&gt;url: https://gitroot.dev/releases/0.4.0/ladybug-0.0.4.wasm&lt;/code&gt; into something like &lt;code&gt;url: https://gitroot.dev/releases/0.4.0/gitroot-ladybug-0.0.4.wasm&lt;/code&gt;, so that a &lt;code&gt;gitroot-*.wasm&lt;/code&gt; is affiliated with me. In the &lt;a href=&quot;plugins/index.html&quot;&gt;registry&lt;/a&gt;, I can show the scope.&lt;/p&gt;

&lt;p&gt;Another example is if someone forks &lt;code&gt;ladybug&lt;/code&gt; and wants to release it in the &lt;a href=&quot;plugins/index.html&quot;&gt;registry&lt;/a&gt;. Previously there was no option to do that, so adding a scope seemed to be the best choice.&lt;/p&gt;

&lt;p&gt;During my research on a way to model this scope, I found the &lt;a target=&quot;_blank&quot; href=&quot;https://ecma-international.org/publications-and-standards/standards/ecma-427/&quot;&gt;ECMA-427 Package-URL (PURL)&lt;/a&gt; specification. PURL stands for Package URL (I call them Plugin URL), a way to express many packages as a URL. What a perfect fit for the GitRoot plugin system!&lt;/p&gt;

&lt;p&gt;This is how a PURL is represented: &lt;code&gt;scheme:type/namespace/name@version?qualifiers#subpath&lt;/code&gt;. In GitRoot, I use only &lt;code&gt;pkg:gitroot/$scope/$name@$version?checksum=$checksum&amp;amp;registry=$urlToDownload&lt;/code&gt; (maybe a default registry like &lt;code&gt;https:%2F%2F%2Fgitroot.dev&lt;/code&gt; will be added in a next version, I&amp;rsquo;m not sure about that for now). There is no specification about what a registry should do to implement the GitRoot specification. For now, GitRoot will download &lt;code&gt;$urlToDownload/$scope-$name-$version.wasm&lt;/code&gt;, and that&amp;rsquo;s all.&lt;/p&gt;

&lt;p&gt;Thanks to PURL, I can delete a lot of other properties in the &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt; file, like version, checksum, and name. I also use it a lot in the code to refer to a plugin. Previously I only used the name, but now I use a full PURL. So, for example, if a plugin wants to call another plugin (thanks to the &lt;code&gt;CallFunc&lt;/code&gt; API), it can define it as &lt;code&gt;pkg:gitroot/$scope/$name&lt;/code&gt; (to mean all possible versions), or an exact version with &lt;code&gt;pkg:gitroot/$scope/$name@$version&lt;/code&gt;. Adding a checksum and even a registry is also possible.&lt;/p&gt;

&lt;p&gt;That was a big refactor to do, but I&amp;rsquo;m pretty happy with the result.&lt;/p&gt;

&lt;h3 id=&quot;gitroot-communication&quot;&gt;GitRoot communication&lt;/h3&gt;

&lt;p&gt;A lot has occured during this summer, on the communication side too.&lt;/p&gt;

&lt;p&gt;I want to thank &lt;a target=&quot;_blank&quot; href=&quot;https://adullact.org/&quot;&gt;Adullact&lt;/a&gt; and particularly Matthieu for letting me present the first show at the &lt;a target=&quot;_blank&quot; href=&quot;https://adullact.org/congres&quot;&gt;Adullact congress&lt;/a&gt;. The video is only in French without subtitles: &lt;a target=&quot;_blank&quot; href=&quot;https://video.adullact.org/w/kYxpgoVZfoPcLsDe19jvQw&quot;&gt;Souveraineté : Savez-vous vraiment qui contrôle vos logiciels ?&lt;/a&gt;. It was a great two-day congress, I talked to a lot of passionate people. And for once, they weren&amp;rsquo;t developers.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://gitroot.dev/branches/v0.5/blog/version-0.5.0-cover.png&quot; alt=&quot;Chart with number of unique visitor, a uge pick (from 500 to 13k) is visible around end of jul start of aout.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Then the first appearance on &lt;a target=&quot;_blank&quot; href=&quot;https://lobste.rs/s/z01edy&quot;&gt;lobste.rs&lt;/a&gt; and &lt;a target=&quot;_blank&quot; href=&quot;https://news.ycombinator.com&quot;&gt;Hacker News&lt;/a&gt; occurred. Thanks &lt;a target=&quot;_blank&quot; href=&quot;https://fosstodon.org/@hasnep&quot;&gt;hasnep&lt;/a&gt; for sharing, you can follow the initial exchange on my &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge/statuses/01KXTPKHP5KVWHETKSMC24B32E&quot;&gt;GoToSocial&lt;/a&gt;. As the image shows, there was a spike in the number of visitors, emails, and questions asked on the platforms and in private. That was a great moment to experience.&lt;/p&gt;

&lt;p&gt;After the mentions on these platforms, some other cool articles popped up (I won&amp;rsquo;t link them as they are principally AI-generated slop). But there was also a video showing how to run GitRoot in a &lt;a target=&quot;_blank&quot; href=&quot;https://nanos.org/&quot;&gt;nanoVM&lt;/a&gt;: &lt;a target=&quot;_blank&quot; href=&quot;https://www.youtube.com/watch?v=g5nW9g5T4wU&quot;&gt;https://www.youtube.com/watch?v=g5nW9g5T4wU&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another particular point of pride was the first mention by &lt;a target=&quot;_blank&quot; href=&quot;https://nesbitt.io/&quot;&gt;Andrew Nesbitt&lt;/a&gt; in the blog article &lt;a target=&quot;_blank&quot; href=&quot;https://nesbitt.io/2026/08/20/issues-in-the-repo.html&quot;&gt;Issues in the Repo&lt;/a&gt;. Thanks to Damien for talking about GitRoot to Andrew. Since you deleted the toot, I guess you don&amp;rsquo;t want to be cited here. If that&amp;rsquo;s not the case, contact me.&lt;/p&gt;

&lt;p&gt;And GitRoot has been added to &lt;a target=&quot;_blank&quot; href=&quot;https://alternativeto.net/software/gitroot/about/&quot;&gt;AlternativeTo&lt;/a&gt;. I don&amp;rsquo;t know if this will lead to anything, but hey, I&amp;rsquo;m right alongside GitHub and GitLab :P&lt;/p&gt;

&lt;h3 id=&quot;sdkgenerator-full-story&quot;&gt;SdkGenerator full story&lt;/h3&gt;

&lt;p&gt;This experiment comes from a bored developer who didn&amp;rsquo;t want to use LLMs, but also didn&amp;rsquo;t want to rewrite the same models/objects/structs (call that what you want) four times. When I want to add a new function to the plugins-sdk, I do it in one language, and then, when it works, I need to rewrite it in all the other languages (more on that in the &amp;ldquo;&lt;a href=&quot;blog/modifying-API-SDK-plugin.html&quot;&gt;Modifying the API in the SDK Plugin&lt;/a&gt;&amp;rdquo; blog article).&lt;/p&gt;

&lt;p&gt;In my day job, I love to build tools that generate code. I have written a tool that takes Kotlin as input and generates TypeScript output. Then I wrote a tool that generates complete Angular components from Kotlin enums (yeah, bored of writing a form select from an enum). And finally, it generates complex Angular services from Spring annotations (when you are very bored, a lot of things can be generated).&lt;/p&gt;

&lt;p&gt;So it&amp;rsquo;s naturally that I started to write a tool that generates TS, Rust, and Zig from Go. But the experience is not conclusive&amp;hellip; A lot of language specifics cannot easily be shared by a tool. I&amp;rsquo;m not giving up&amp;hellip; I just have more interesting priorities (yes, I&amp;rsquo;m less bored on GitRoot than at my work ^^). But if this is the kind of thing you like, yes, you, reading that, please reach out to me, and I&amp;rsquo;ll explain all that I did and how you can take it further.&lt;/p&gt;

&lt;p&gt;By the way, I discovered another kind of tool that does a similar job: &lt;a target=&quot;_blank&quot; href=&quot;https://component-model.bytecodealliance.org/design/wit.html&quot;&gt;WIT&lt;/a&gt;. It&amp;rsquo;s not an official spec from the W3C, but since it&amp;rsquo;s from the Bytecode Alliance, it&amp;rsquo;s big enough to use. I have quickly looked at it, and the engine I use for Wasm is not really compatible for now. But I&amp;rsquo;m keeping that in mind; it could be a replacement for &lt;code&gt;SdkGenerator&lt;/code&gt; if nobody (including me) wants to do the job.&lt;/p&gt;

&lt;h3 id=&quot;max-anonymous-branches&quot;&gt;Max anonymous branches&lt;/h3&gt;

&lt;p&gt;When a lot of people started to talk about GitRoot, one thing haunted my nights: what if someone builds a simple script that clones GitRoot, creates 1,000 branches, and pushes them? I would need to &lt;code&gt;git push origin :branch&lt;/code&gt; for every single one of them. You don&amp;rsquo;t need to register to contribute to GitRoot, and that&amp;rsquo;s one of its strengths, but also one of its weaknesses.&lt;/p&gt;

&lt;p&gt;How to make that impossible without blocking all the beauty of GitRoot? One day, a simple solution came to me (yes, I love imagining scenarios running against me). Why not limit the number of branches from anonymous users? It&amp;rsquo;s simple after all: I need to count the number of branches made by anonymous users, and if it exceeds a limit, I stop accepting them. However, people I have added to my repo because of a previous contribution can still push new branches. That is what I added, but&amp;hellip; there is always a but&amp;hellip; counting the number of branches is lightweight, but counting the number of branches from an anonymous user is really expensive. Life is full of choices, and I made one.&lt;/p&gt;

&lt;p&gt;When the branch limit is reached, we stop accepting anonymous contributions. That means if you set 10 as the maximum and you make 20 branches, nobody else can contribute. Yes, it&amp;rsquo;s not perfect, but it&amp;rsquo;s way better than having nightmares every night.&lt;/p&gt;

&lt;p&gt;If an admin configures the forge with &lt;code&gt;NbMaxBranchesPerProjectBeforeRejectingAnonymous=10&lt;/code&gt;, that means when a project has 10 branches (who added them doesn&amp;rsquo;t matter), the next anonymous user (not present in &lt;code&gt;.gitroot/users.yml&lt;/code&gt;) will be rejected. Of course, all known users can continue to create as many branches as they want.&lt;/p&gt;

&lt;p&gt;A project can set a lower number, &lt;code&gt;NbMaxBranchesBeforeRejectingAnonymous=5&lt;/code&gt;, but if the number is bigger, it will have no effect (e.g., if set to &lt;code&gt;NbMaxBranchesBeforeRejectingAnonymous=15&lt;/code&gt;, GitRoot will use 10 as set by the forge administrator).&lt;/p&gt;

&lt;p&gt;A neat trick: setting &lt;code&gt;NbMaxBranchesBeforeRejectingAnonymous=0&lt;/code&gt; blocks all anonymous pushes. Alternatively, you can use the &lt;a href=&quot;doc/how-tos/no_anonymous_user.html&quot;&gt;Not accepting anonymous contribution&lt;/a&gt; guide. The main difference is the error message: the first gives &lt;code&gt;anonymous branches not allowed&lt;/code&gt;, while the latter shows &lt;code&gt;you can&#39;t write in $branchName&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;zig-plugin-sdk&quot;&gt;Zig plugin-sdk&lt;/h3&gt;

&lt;p&gt;With every release, I try to add a new plugin-sdk language. This time I picked &lt;a target=&quot;_blank&quot; href=&quot;https://ziglang.org/&quot;&gt;zig&lt;/a&gt; because it is a language that natively manages Wasm and WASI, so I was pretty sure I would finish this implementation.&lt;/p&gt;

&lt;p&gt;I loved the error system, compared to Rust (optionals) and Go (multiple returns), Zig chose an in-between solution with &lt;code&gt;!&lt;/code&gt; and some language keywords. But what totally blew my mind is the &lt;code&gt;comptime&lt;/code&gt; feature, where the compiler can become an interpreter for compilation output. Amazing idea, I am pretty sure a lot of things will follow this path.&lt;/p&gt;

&lt;p&gt;Long story short, I spent a long time discovering the language, and it was a very good time for me (it changes your perspective, seeing the same problem solved another way)&amp;hellip; but nothing related directly to GitRoot.&lt;/p&gt;

&lt;p&gt;As with all the plugin-sdks, there is unfortunately no way for you to use it yet. I don&amp;rsquo;t release them in registries until the API is stable, and a lot of breaking changes are coming in the next release.&lt;/p&gt;

&lt;h3 id=&quot;pluginsdk-improvements&quot;&gt;PluginSdk improvements&lt;/h3&gt;

&lt;p&gt;Regarding the plugin-sdk, here are a lot of new capabilities for plugins to use:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;schedule&lt;/code&gt;: you can allow a plugin to be called at a scheduled time. The format is a cron expression with 5 segments as &lt;code&gt;&amp;lt;minute&amp;gt; &amp;lt;hour&amp;gt; &amp;lt;day&amp;gt; &amp;lt;month&amp;gt; &amp;lt;weekday&amp;gt;&lt;/code&gt;. Example:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;purl&lt;/span&gt;: pkg:gitroot/gitroot/firefly@0.0.1?checksum=sha256:fae956f5cc9239a87b1c2a149a5466fec85476377de4ada33dda190edb3700c4&amp;amp;registry=https:%2F%2Fgitroot.dev%2Freleases%2F0.5.0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;active&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;schedule&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;* * * * *&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That means the firefly plugin will be executed on the main branch every minute. Refer to the &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/adhocore/gronx#cron-expression&quot;&gt;gronx library documentation&lt;/a&gt; for cron expression details.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;httpClient&lt;/code&gt;: plugins can make HTTP requests. I have added standard verbs (&lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt; and &lt;code&gt;DELETE&lt;/code&gt;), if you need more, please let me know. You can also add headers as a &lt;code&gt;map[string][]string&lt;/code&gt;. In the next version, I will add permission a plugin must request beforehand.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;callFunc&lt;/code&gt; configuration: you can add a configuration for the called plugin within the caller plugin&amp;rsquo;s &lt;code&gt;callFunc&lt;/code&gt;. This makes it possible to tell the called plugin what its configuration is in this context. Previously, an empty config was set, so the plugin&amp;rsquo;s defaults (if any) were used. Example:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;callFunc&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;pluginPURL&lt;/span&gt;: pkg:gitroot/gitroot/apex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;funcName&lt;/span&gt;: renderHtml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;branchesDir&lt;/span&gt;: branches
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;favicon&lt;/span&gt;: logo.png
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;footer&lt;/span&gt;: &amp;lt;p&amp;gt;Start with &amp;lt;code&amp;gt;git clone ssh://gitroot.dev/&amp;lt;/code&amp;gt; 🚀&amp;lt;/p&amp;gt;&amp;lt;small&amp;gt;Hosted with ❤️ by Gitroot&amp;lt;/small&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;generateGitWorktree&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt;9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;header&lt;/span&gt;: ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;init&lt;/code&gt; function, one to rule them all: I have refactored the &lt;code&gt;init()&lt;/code&gt; function of the plugin-sdk to provide an &lt;code&gt;initKind model.InitKind&lt;/code&gt;, which can be &lt;code&gt;confhaschange&lt;/code&gt;, &lt;code&gt;diff&lt;/code&gt;, &lt;code&gt;schedule&lt;/code&gt;, or &lt;code&gt;funccall&lt;/code&gt;. This lets the plugin know in which context it is running, and this architecture allows future hooks to be added without breaking the API.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pull-iterator&lt;/code&gt;: I have introduced pull-iterators to be able to group reports without sending all reports to the Wasm memory. In a future version, I will use this mechanism to transfer commits and files directly to Wasm.&lt;/p&gt;

&lt;h3 id=&quot;sftp-feature&quot;&gt;Sftp feature&lt;/h3&gt;

&lt;p&gt;Users can access every repository&amp;rsquo;s web directory through SFTP. To do this, you can use the same SSH key that you use for your Git repository. When you log in with &lt;code&gt;sftp -P $SshPort $SshAddress:/$repo/&lt;/code&gt; (like &lt;code&gt;sftp -P 4545 127.0.0.1:/repo1/&lt;/code&gt;), you will see the list of web representations for your repository. Anyone can do this, but only those in the &amp;ldquo;owner&amp;rdquo; section of &lt;code&gt;.gitroot/users.yml&lt;/code&gt; have write access.&lt;/p&gt;
&lt;blockquote style=&quot;border-color: #006ac7&quot;&gt;

&lt;p&gt;Be careful: this feature is in alpha, so don&amp;rsquo;t write scripts around it for now. I will probably change the directory hierarchy &lt;a href=&quot;issues/d42a-sftp-better-tree.html&quot;&gt;#d42a&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To enable this feature, you need to change &lt;code&gt;activesftp: false&lt;/code&gt; to &lt;code&gt;activesftp: true&lt;/code&gt; in &lt;code&gt;.gitroot/forgeConfig.yml&lt;/code&gt; at the root repository of your instance. Don&amp;rsquo;t forget to restart GitRoot after that, otherwise the parameter will not be taken into account by GitRoot.&lt;/p&gt;

&lt;h3 id=&quot;some-interesting-bug-fixes&quot;&gt;Some interesting bug fixes&lt;/h3&gt;

&lt;p&gt;Of course, a lot of bug fixes have been included in this release. I will present only the most interesting ones, and you can find a more complete list in the &lt;a href=&quot;CHANGELOG.html&quot;&gt;changelog&lt;/a&gt; and an even more thorough one in the commits.&lt;/p&gt;

&lt;p&gt;The first annoying bug occurred when &lt;a href=&quot;plugins/name/grafter.html&quot;&gt;Grafter&lt;/a&gt; committed to a file, but &lt;a href=&quot;plugins/name/apex.html&quot;&gt;Apex&lt;/a&gt; did not render that file if the Grafter plugin was placed after Apex in the configuration. In fact, even if you put Grafter before Apex, the bug persisted because Apex &lt;code&gt;reports&lt;/code&gt; the rendering back to Grafter, meaning it missed that final report. Now, I have added a subsequent loop where plugins can only write to the web directory (and cache) but not to Git. This way, all plugins loop over commits and files to generate new commits, and only afterwards do we loop through all plugins to see if they have anything to write to the web (or cache) excluding Git to avoid an infinite loop.&lt;/p&gt;

&lt;p&gt;Another annoying issue happened when you changed a plugin&amp;rsquo;s configuration, causing it to enter a worktree loop (looping over files in the worktree instead of commits). Consequently, the commits generated by this loop were not propagated to other plugins, causing widespread rendering misses on the web.&lt;/p&gt;

&lt;p&gt;Finally, if you decided to add a plugin multiple times in your &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt; file (to have multiple configurations, for instance), only the first instance was actually loaded and executed. Now, you can include a plugin as many times as you want, with any configuration you choose, and they will all be executed.&lt;/p&gt;

&lt;h2 id=&quot;what-next&quot;&gt;What next?&lt;/h2&gt;

&lt;p&gt;New plugins, bug fixes, updates&amp;hellip; Yes, until now this article was only about GitRoot, but what is GitRoot without its plugins? The immediate next thing I will do is write an article about what new plugin versions can do.&lt;/p&gt;

&lt;p&gt;Then I will probably start working on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discover GitRoot without installing anything.&lt;/li&gt;
&lt;li&gt;Create your own plugins with ease.&lt;/li&gt;
&lt;li&gt;Transform GitRoot into a registry for (at least) Go, Rust, NPM and Zig.&lt;/li&gt;
&lt;li&gt;Federation with GitRoot but also other forges.&lt;/li&gt;
&lt;li&gt;Implement something for users (how to represent them).&lt;/li&gt;
&lt;li&gt;Manage more Git functions in plugins (branch, pull, push, rebase).&lt;/li&gt;
&lt;li&gt;Use pull-iterators to stabilise the plugin-sdk.&lt;/li&gt;
&lt;li&gt;Easier configuration of plugins.&lt;/li&gt;
&lt;li&gt;Auto-update of plugins.&lt;/li&gt;
&lt;li&gt;New language plugin-sdk.&lt;/li&gt;
&lt;li&gt;Air-gapped version of GitRoot.&lt;/li&gt;
&lt;li&gt;Make every repository a gemini capsule.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you compare this with the &lt;a href=&quot;blog/version-0.4.0.html#what-next&quot;&gt;last v0.4 what next&lt;/a&gt; section, there are a lot more things. Every time I finish a feature (or don&amp;rsquo;t, see the &lt;a href=&quot;blog/version-0.5.0.html#sdkgenerator-full-story&quot;&gt;plugin-sdk generator&lt;/a&gt;), I can add ten more new features to do. That&amp;rsquo;s why I love GitRoot, and that&amp;rsquo;s why &lt;code&gt;I Move slowly, and take care of things&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Special thanks to &lt;code&gt;Ryuno-Ki&lt;/code&gt;, &lt;code&gt;Matthieu&lt;/code&gt; and &lt;code&gt;ks&lt;/code&gt; from the &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/#gitroot:matrix.org&quot;&gt;#gitroot:matrix.org&lt;/a&gt; Matrix room for reviewing and correcting this article. Want to help with future posts? Join us in the room!&lt;/em&gt;&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.5.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.4.0.md</link><title>0.4.0</title><pubDate>2026/04/22</pubDate><description>&lt;h1 id=&quot;the-teenager-starts-building-its-own-future&quot;&gt;The teenager starts building its own future&lt;/h1&gt;

&lt;h2 id=&quot;if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes&lt;/h2&gt;

&lt;p&gt;A lot has been done in this new release of GitRoot. When I saw other FOSS projects claiming &amp;ldquo;this is the biggest release&amp;rdquo;, I thought it was a joke. But in fact, it&amp;rsquo;s true: every new release gets bigger and bigger. It&amp;rsquo;s just astonishing to start a sprint with almost zero ideas and, 4 months later, see all of this accomplished.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you skim this article, don&amp;rsquo;t forget to check out the conclusion about &lt;a href=&quot;blog/version-0.4.0.html#what-next&quot;&gt;&amp;ldquo;what&amp;rsquo;s next?&amp;rdquo;&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;the-thing-i-m-most-proud-of&quot;&gt;The thing i&amp;rsquo;m most proud of&lt;/h3&gt;

&lt;p&gt;Executors can finally build GitRoot! As I love to say: GitRoot can build GitRoot on GitRoot. And it was a pain to do. &lt;a href=&quot;blog/version-0.4.0.html#executor&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-thing-with-the-most-visibility&quot;&gt;The thing with the most visibility&lt;/h3&gt;

&lt;p&gt;The plugin registry is here! After a lot of reading and reflecting, I started this &amp;ldquo;epic story&amp;rdquo;. Today is only the beginning, but you can now find plugins directly at &lt;a href=&quot;plugins/index.html&quot;&gt;gitroot.dev/plugins&lt;/a&gt;. What&amp;rsquo;s next? &lt;a href=&quot;blog/version-0.4.0.html#plugin-registry&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-thing-that-changes-everything-but-was-so-simple&quot;&gt;The thing that changes everything but was so simple&lt;/h3&gt;

&lt;p&gt;Previously, a branch (and graft) couldn&amp;rsquo;t be rendered on the web. The problem was: if I have an &lt;code&gt;index.md&lt;/code&gt; in my git that renders at &lt;code&gt;myinstance.com/index.html&lt;/code&gt;, anyone can create a branch. If a &amp;ldquo;bad user&amp;rdquo; comes along, changes everything in this file, creates a branch, and pushes for review, where should this file render? Certainly not at &lt;code&gt;myinstance.com/index.html&lt;/code&gt;. &lt;a href=&quot;blog/version-0.4.0.html#apex-render-branch&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-thing-that-was-more-complex-than-i-thought&quot;&gt;The thing that was more complex than I thought&lt;/h3&gt;

&lt;p&gt;Apex was a big plugin. Basically, it manages the rendering of your git files to the web. But over time, it started to grow out of control because of markdown, code, worktrees, emojis&amp;hellip; A simple (in theory) solution was to split it into multiple plugins. &lt;a href=&quot;blog/version-0.4.0.html#plugin-deps&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-things-i-m-just-happy-are-done&quot;&gt;The things I&amp;rsquo;m just happy are done&lt;/h3&gt;

&lt;p&gt;No more mandatory signed commits! Well, it&amp;rsquo;s still better if you sign them, but this change means the &lt;code&gt;.gitroot/init.sh&lt;/code&gt; has been &lt;del&gt;deleted&lt;/del&gt; moved to a plugin. &lt;a href=&quot;blog/version-0.4.0.html#sigma-plugin&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Moving a file from git to web was hard, deleting a file was &amp;ldquo;not implemented&amp;rdquo;, and a lot of APIs were missing in the plugin-sdk. I got my hands dirty with all that, and now everything is running smoothly. &lt;a href=&quot;blog/version-0.4.0.html#fs-plugin-sdk&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;in-depth-details&quot;&gt;In-depth Details&lt;/h2&gt;

&lt;h3 id=&quot;executor&quot;&gt;Executor&lt;/h3&gt;

&lt;p&gt;When I started this feature, I just wanted a lightweight container to run &lt;code&gt;make build&lt;/code&gt; and output the result as a &lt;code&gt;gitroot-next&lt;/code&gt; canary binary. If I had known how much time and energy it would actually cost, I’m not sure I would have taken this road.&lt;/p&gt;

&lt;p&gt;At first, I chose &lt;a href=&quot;doc/technicals/executors.html#bwrap-executor&quot;&gt;bubblewrap&lt;/a&gt; as a lightweight container, and immediately started building a &lt;a href=&quot;doc/technicals/executors.html#container-executor&quot;&gt;container&lt;/a&gt; version. Then I realized I needed a &lt;a href=&quot;doc/technicals/executors.html#baremetal-executor&quot;&gt;bareMetal&lt;/a&gt; one. And then, I needed a way to run it on other machines, so &lt;a href=&quot;doc/technicals/executors.html#ssh-executor&quot;&gt;ssh&lt;/a&gt; came along.&lt;/p&gt;

&lt;p&gt;But when I launched it for the first time, I discovered that having &lt;code&gt;make&lt;/code&gt; isn&amp;rsquo;t enough for GitRoot, I need Golang, Rust, TinyGo, and so on. &amp;ldquo;No problem,&amp;rdquo; I thought, &amp;ldquo;I&amp;rsquo;ll use &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt; to manage that.&amp;rdquo; But running &lt;code&gt;mise install&lt;/code&gt; takes a lot of time (like 10-15 minutes on my computer). So, I built a cache system to ensure that hit only happens the first time.&lt;/p&gt;

&lt;p&gt;Still not the end! After that, I needed to configure port numbers, so I built a complete environment variable system, and used it to configure some &lt;code&gt;mise&lt;/code&gt; paths.&lt;/p&gt;

&lt;p&gt;Is it finished? Not yet! To be able to move a binary generated from a bwrap environment to the web, it has to travel a lot. And by &amp;ldquo;a lot,&amp;rdquo; I mean moving it to a place where the plugin that launched the executor can access it: its own cache. Then the plugin needs to move it from there to the web. No problem, I just added a bunch of &lt;a href=&quot;blog/modifying-API-SDK-plugin.html&quot;&gt;SDK API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After all that, some commands finally started working&amp;hellip; well, kind of. When I ran a bwrap or a container command, I couldn&amp;rsquo;t get the stats of what was happening inside. Having process stats is something I’m proud of (even if it’s still evolving in future versions), but seeing that bwrap reported 20 MB of RAM while the sub-process was actually consuming 80 MB was very frustrating. Furthermore, I needed a way to extract stats from the remote SSH executor.&lt;/p&gt;

&lt;p&gt;I tried a lot of things, but ultimately, I chose to build an &lt;a href=&quot;app/executor/main.go&quot;&gt;executor CLI&lt;/a&gt; to handle it. I now inject that binary inside your bwrap, container, or SSH environment. This little CLI launches commands, gathers system stats, command status, and logs. It packs everything into a well-formatted JSON and returns it to the executor in GitRoot. GitRoot then manages artifacts/caches and hands the result back to the calling plugin (probably &lt;a href=&quot;plugins/name/hop.html&quot;&gt;hop&lt;/a&gt;), which tells you what’s happening. In this case, it reports back so the &lt;a href=&quot;plugins/name/grafter.html&quot;&gt;grafter plugin&lt;/a&gt; can include the info in the current graft.&lt;/p&gt;
&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;295.9538&quot; height=&quot;429.0792&quot; viewBox=&quot;0 0 295.9538 429.0792&quot;&gt;&lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;295.9538&quot; height=&quot;429.0792&quot; fill=&quot;transparent&quot;/&gt;&lt;defs&gt;&lt;marker id=&quot;arrow-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 10 5 L 0 10 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-start-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;4.5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 5 L 10 10 L 10 0 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;path id=&quot;edge-0&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-0&quot; d=&quot;M 210.589,59.000 L 210.589,107.236&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot; x=&quot;105.97&quot; y=&quot;62.00&quot; width=&quot;90.62&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;151.28&quot; y=&quot;79.70&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;151.28&quot; dy=&quot;0.00&quot;&gt;Exec cmds&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-1&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-1&quot; d=&quot;M 210.679,158.236 L 210.679,206.472&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;path id=&quot;edge-2&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-2&quot; d=&quot;M 210.835,257.472 L 210.835,305.708&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-2&quot; data-label-kind=&quot;center&quot; x=&quot;17.60&quot; y=&quot;260.47&quot; width=&quot;179.24&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-2&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;107.22&quot; y=&quot;278.17&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;107.22&quot; dy=&quot;0.00&quot;&gt;bwrap/container/ssh&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-3&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-3&quot; d=&quot;M 210.955,356.708 L 210.955,370.079&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;path id=&quot;edge-4&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-4&quot; d=&quot;M 166.834,395.579 L 153.002,395.579 L 120.042,347.208 L 133.874,347.208&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;path id=&quot;edge-5&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-5&quot; d=&quot;M 133.874,315.208 L 8.000,315.208 L 8.000,247.972 L 165.077,247.972&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-5&quot; data-label-kind=&quot;center&quot; x=&quot;46.42&quot; y=&quot;258.31&quot; width=&quot;206.12&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-5&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;149.48&quot; y=&quot;276.01&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;149.48&quot; dy=&quot;0.00&quot;&gt;status/code/logs/stats&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-6&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-6&quot; d=&quot;M 165.077,215.972 L 41.355,215.972 L 41.355,148.736 L 153.161,148.736&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-6&quot; data-label-kind=&quot;center&quot; x=&quot;56.06&quot; y=&quot;159.07&quot; width=&quot;206.12&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-6&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;159.12&quot; y=&quot;176.77&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;159.12&quot; dy=&quot;0.00&quot;&gt;status/code/logs/stats&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-7&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-7&quot; d=&quot;M 153.161,116.736 L 26.706,116.736 L 26.706,33.500 L 167.201,33.500&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-7&quot; data-label-kind=&quot;center&quot; x=&quot;57.12&quot; y=&quot;59.83&quot; width=&quot;206.12&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-7&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;160.18&quot; y=&quot;77.53&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;160.18&quot; dy=&quot;0.00&quot;&gt;status/code/logs/stats&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;rect x=&quot;167.20&quot; y=&quot;8.00&quot; width=&quot;86.75&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.58&quot; y=&quot;37.00&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.58&quot; dy=&quot;0.00&quot;&gt;Hop&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;153.16&quot; y=&quot;107.24&quot; width=&quot;114.88&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.60&quot; y=&quot;136.24&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.60&quot; dy=&quot;0.00&quot;&gt;GitRoot&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;133.87&quot; y=&quot;305.71&quot; width=&quot;154.08&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.91&quot; y=&quot;334.71&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.91&quot; dy=&quot;0.00&quot;&gt;executor cli&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;166.83&quot; y=&quot;370.08&quot; width=&quot;88.32&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;211.00&quot; y=&quot;399.08&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;211.00&quot; dy=&quot;0.00&quot;&gt;cmd&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;165.08&quot; y=&quot;206.47&quot; width=&quot;91.36&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.76&quot; y=&quot;235.47&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.76&quot; dy=&quot;0.00&quot;&gt;exec&lt;/tspan&gt;&lt;/text&gt;&lt;g transform=&quot;translate(210.59 107.24) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(210.68 206.47) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(210.84 305.71) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(210.95 370.08) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(133.87 347.21) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(165.08 247.97) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(153.16 148.74) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(167.20 33.50) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;/svg&gt;
&lt;p&gt;Phew, is that enough now? It should be, but we&amp;rsquo;ll see how it performs in real-time on &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev&quot;&gt;gitroot.dev&lt;/a&gt;. Not everything is finished: for example, the SSH executor doesn’t manage cache yet &lt;a href=&quot;issues/94af-executor-cache.html&quot;&gt;#94af&lt;/a&gt; and can&amp;rsquo;t use the container strategy &lt;a href=&quot;issues/f050-ssh-executor-subCmd.html&quot;&gt;#f050&lt;/a&gt;. You can’t run the executor on other architectures &lt;a href=&quot;issues/4d49-executor-compile-other-archs.html&quot;&gt;#4d49&lt;/a&gt; yet, and I’m sure you’ll soon want a way to clean up temp directories &lt;a href=&quot;issues/647f-clean-executor-temp-dir.html&quot;&gt;#647f&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But hey, let’s celebrate what we’ve built before jumping into the next sprint!&lt;/p&gt;

&lt;h3 id=&quot;plugin-registry&quot;&gt;Plugin registry&lt;/h3&gt;

&lt;p&gt;Until now, explaining GitRoot was always about &amp;ldquo;data in git and the plugin system&amp;rdquo;, but what exactly are the plugins? Where are they? What can they do? All of that was a bit mysterious. With this milestone, I’m answering some of those questions with a first shot at a registry. Be careful, though: this first version is just a &amp;ldquo;hand-written&amp;rdquo; preview of what it could be in the future. If you look at the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/plugins/index.md&quot;&gt;source&lt;/a&gt;, you’ll see that everything is ready to be automated, but nothing is&amp;hellip; yet.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;m not entirely sure what you, as a consumer, want to see here. Probably an explanation of what the plugin does, some docs, and the available versions. But as a plugin creator, you likely want to manage all of that in your own repository instead of the &lt;a href=&quot;plugins/index.html&quot;&gt;gitroot.dev registry&lt;/a&gt;. I chose to keep the registry very light and put the details directly in the &lt;a href=&quot;app/plugins/apex/index.html&quot;&gt;plugin&amp;rsquo;s directory&lt;/a&gt; to give you an idea of what your own repo could look like.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ll need to iterate on this. Even if I choose to embed package details in the registry, I still need to decide how: maybe via a JSON in your repo, maybe in the registry itself, or perhaps inside your package (WASM)? I don&amp;rsquo;t know yet what’s simplest for you and for the registry.&lt;/p&gt;

&lt;p&gt;Anyway, I hope this registry helps you understand what a plugin is and how it shapes your forge. Some are small (&lt;a href=&quot;plugins/name/ladybug.html&quot;&gt;ladybug&lt;/a&gt;), others are massive (&lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex&lt;/a&gt;), and some aren&amp;rsquo;t usable solo as they are &lt;a href=&quot;blog/version-0.4.0.html#plugin-deps&quot;&gt;dependencies&lt;/a&gt; for others (like &lt;a href=&quot;plugins/name/apex_mermaid.html&quot;&gt;apx_mermaid&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Since plugins are now decoupled from GitRoot, I’ve moved all changelogs from the &lt;a href=&quot;CHANGELOG.html&quot;&gt;general&lt;/a&gt; one to the individual plugin directories. This way, I’ll be able to release plugin versions independently of GitRoot (and vice versa). I hope to break the plugin API less often, even if I know I probably will in the near future. All of this paves the way for you to build your own plugins and I hope to see them in the gitroot.dev registry soon. Or, of course, you can just build your own registry!&lt;/p&gt;

&lt;h3 id=&quot;apex-render-branch&quot;&gt;Apex render branch&lt;/h3&gt;

&lt;p&gt;For as long as I can remember, this has been a problem with GitRoot: how to render arbitrary changes, especially deletions, on the web? I had imagined a lot of strategies. The last one was to render inside a directory named after the git tree ID (the abstract representation of a working directory). Then, I’d create a symlink between that tree and a main directory.&lt;/p&gt;

&lt;p&gt;The idea was: if you push a file in a branch, git gives you a tree ID like &lt;code&gt;a654fs&lt;/code&gt;. It would be represented as &lt;code&gt;mydomain.com/myBranch/file -&amp;gt; mydomain.com/tree/a654fs/file&lt;/code&gt;. When you merged that branch, GitRoot would simply symlink the main directory to it: &lt;code&gt;mydomain.com/file -&amp;gt; mydomain.com/tree/a654fs/file&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Elegant, but honestly, a total over-engineered mess. When I actually sat down to code this, I ended up adding just two lines to the &lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex plugin&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; p.currentCommit.Branch &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; p.currentCommit.Branch &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; p.config.defaultBranch {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	path = filepath.&lt;span style=&quot;color:#50fa7b&quot;&gt;Join&lt;/span&gt;(p.config.branchesDir, p.currentCommit.Branch, path)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And&amp;hellip; yeah, that was enough. I&amp;rsquo;ve been overthinking this since the very beginning of GitRoot! Rendering files from branches into a dedicated branches directory&amp;hellip; It&amp;rsquo;s simple. Just like GitRoot should be.&lt;/p&gt;

&lt;p&gt;So, what does it mean in practice? The file is now rendered at &lt;code&gt;myinstance.com/mybranch/index.html&lt;/code&gt; and the graft at &lt;code&gt;myinstance.com/mybranch/grafts/mybranch.html&lt;/code&gt;. How did I not think of something this simple before?&lt;/p&gt;

&lt;h3 id=&quot;plugin-deps&quot;&gt;Plugin deps&lt;/h3&gt;

&lt;p&gt;The catalyst for this development was the &lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex plugin&lt;/a&gt;. At over 3 MB, it was by far the largest plugin. It was packed with configuration, specifically a bunch of booleans to &lt;a href=&quot;issues/8b29-apex-working-tree.html&quot;&gt;toggle features&lt;/a&gt;. Furthermore, I needed to render Markdown in other plugins, like &lt;a href=&quot;plugins/name/pollen.html&quot;&gt;pollen&lt;/a&gt;, to create a proper &lt;a href=&quot;issues/83d8-pollen-content-flux.html&quot;&gt;RSS feed&lt;/a&gt;. It became clear to me: this plugin needed to be split into smaller, more specialized ones.&lt;/p&gt;

&lt;p&gt;So, I started building the &lt;a href=&quot;plugins/name/apex_markdown.html&quot;&gt;apex_markdown plugin&lt;/a&gt;, then &lt;a href=&quot;plugins/name/apex_code.html&quot;&gt;apex_code&lt;/a&gt;, and next week &lt;a href=&quot;plugins/name/apex_mermaid.html&quot;&gt;apex_mermaid&lt;/a&gt;. But what I hadn&amp;rsquo;t fully anticipated was the sheer amount of work required to make it all work together.&lt;/p&gt;

&lt;p&gt;What if a dependency is missing? What if a method is deprecated or removed in a newer version? To handle this, I’ve chosen to make all dependencies entirely optional for now. It’s an easy choice to make today, but I might change my mind tomorrow, after all, there’s a reason most package managers handle version ranges and resolution.&lt;/p&gt;

&lt;p&gt;This new architecture allows for adding tons of features without rebuilding an entire plugin. Imagine adding Org-mode or RST support: you just swap out &lt;a href=&quot;plugins/name/apex_markdown.html&quot;&gt;apex_mardown&lt;/a&gt; instead of touching the whole stack. It also allows other plugins to reuse existing behaviors effortlessly.&lt;/p&gt;

&lt;p&gt;With this in place, I’m not even sure if I’ll keep the &lt;code&gt;report&lt;/code&gt; system, which was an other way of passing information between plugins.&lt;/p&gt;

&lt;h3 id=&quot;sigma-plugin&quot;&gt;Sigma plugin&lt;/h3&gt;

&lt;p&gt;Signed commits should be the default in git today. With SSH signing, it’s easy to do, and everyone should use it because impersonating a name and email is way too easy in git. When I started GitRoot, I made signed commits mandatory before allowing a push. But in fact, making signatures mandatory should be a choice for the instance owner. GitRoot is all about the freedom to choose how your forge works for you.&lt;/p&gt;

&lt;p&gt;Furthermore, a friend told me he preferred signing with PGP and already had his setup ready. So why force him to use a &lt;code&gt;.gitroot/init.sh&lt;/code&gt;? It’s frustrating when you want to test a new forge and you’re forced to run random sh commands, even if it&amp;rsquo;s just to configure your git client.&lt;/p&gt;

&lt;p&gt;With this release, I have completely removed &lt;code&gt;init.sh&lt;/code&gt; from GitRoot itself. But of course, as always, I’ve created a plugin to &amp;ldquo;almost&amp;rdquo; recreate this feature. The only difference is: instead of refusing your commit when you push, the &lt;a href=&quot;plugins/name/stigma.html&quot;&gt;stigma plugin&lt;/a&gt; will simply report unsigned or badly signed commits in your graft.&lt;/p&gt;

&lt;p&gt;Now, you have the choice to accept unsigned commits in your repo. You can even ignore the whole signing topic entirely if you don&amp;rsquo;t install the stigma plugin.&lt;/p&gt;

&lt;p&gt;That said, I’m still convinced that signing commits should remain the default.&lt;/p&gt;

&lt;h3 id=&quot;fs-plugin-sdk&quot;&gt;FS plugin-sdk&lt;/h3&gt;

&lt;p&gt;After finishing all the &lt;a href=&quot;blog/version-0.4.0.html#executor&quot;&gt;executors features&lt;/a&gt;, one thing was still missing: the ability for a plugin to move a file from one path to another. As explained in this &lt;a href=&quot;blog/modifying-API-SDK-plugin.html&quot;&gt;blog post&lt;/a&gt;, even though a plugin sees one single filesystem, GitRoot actually uses three different ones: one for cache (a directory), one for the web (a subdirectory), and a virtual one for the git working directory.&lt;/p&gt;

&lt;p&gt;I was missing several methods in both the Rust and TS SDKs, and the existing API was poorly designed with inconsistent naming. I decided to redesign everything and added a lot of new methods accessible via &lt;code&gt;FS&lt;/code&gt; in the SDK. Now, you can copy, move, delete, replace, and write files in every filesystem. Moving and copying can now be done between different filesystems, making it easy to make a file accessible on the web even if it originated from git or the cache.&lt;/p&gt;

&lt;p&gt;I have marked all old methods as deprecated, and they will be removed in the next version of GitRoot (or more specifically, the plugin-sdk, though they share the same release number for now).&lt;/p&gt;

&lt;h2 id=&quot;what-next&quot;&gt;What next?&lt;/h2&gt;

&lt;p&gt;I have a massive roadmap for GitRoot (in no particular order):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discover GitRoot without installing anything.&lt;/li&gt;
&lt;li&gt;Create your own plugins with ease.&lt;/li&gt;
&lt;li&gt;Transform GitRoot into a registry for (at least) Go, Rust, and NPM.&lt;/li&gt;
&lt;li&gt;Plugins should be usable via PURL.&lt;/li&gt;
&lt;li&gt;Plugins can send/receive HTTP requests.&lt;/li&gt;
&lt;li&gt;Plugins can be scheduled.&lt;/li&gt;
&lt;li&gt;Rebase go-git to take advantage of their performance improvements.&lt;/li&gt;
&lt;li&gt;Generate the plugin SDK automatically.&lt;/li&gt;
&lt;li&gt;Add plugin SDKs for (at least) Zig, Kotlin, and Python.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yeah, I could go on all night. And those are just core GitRoot features. I haven&amp;rsquo;t even mentioned making instances to build projects in hosted environments. I haven&amp;rsquo;t talked about specific plugins (sending a &amp;ldquo;toot&amp;rdquo; on commit, managing secrets with age, translating your app&amp;hellip;). I haven&amp;rsquo;t even touched on federating GitRoot with other GitRoot instances, or with Forgejo, SourceHut, or Radicle.&lt;/p&gt;

&lt;p&gt;All of that is possible, and I’m confident it will be accessible in the future. But &amp;ldquo;when&amp;rdquo;? That’s the real question. If you think you can help in any way (coding, of course, but also management, communication, or just to brainstorm), please &lt;a href=&quot;contact.html&quot;&gt;let me know&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is only the beginning. GitRoot will get better and better with every release.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.4.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.3.0.md</link><title>0.3.0</title><pubDate>2025/12/20</pubDate><description>&lt;h1 id=&quot;from-baby-to-teenager-gitroot-become-a-forge&quot;&gt;From &amp;ldquo;baby to teenager&amp;rdquo; GitRoot become a forge&lt;/h1&gt;

&lt;h2 id=&quot;if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes&lt;/h2&gt;

&lt;p&gt;Previously, GitRoot served as a &lt;strong&gt;Source Code Management (SCM)&lt;/strong&gt; system, allowing you to create Git repositories and manage access permissions. However, GitRoot was never intended to be &lt;em&gt;just&lt;/em&gt; an SCM. With this &lt;strong&gt;v0.3.0&lt;/strong&gt; release, it evolves into a full-fledged &lt;strong&gt;forge&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To activate this new capability on your instance, &lt;a href=&quot;blog/version-0.3.0.html#for-admin-activating-an-executor&quot;&gt;follow the administrator section&lt;/a&gt;. To start using it as a user, &lt;a href=&quot;blog/version-0.3.0.html#for-user-using-the-hop-plugin&quot;&gt;see the user section&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The introduction of &lt;strong&gt;executors&lt;/strong&gt; brings a crucial new feature: the &lt;a href=&quot;blog/version-0.3.0.html#plugin-api-report-and-reporter&quot;&gt;report and reporter plugin API&lt;/a&gt;. This API allows any plugin to provide informational feedback to the user. &lt;strong&gt;Important:&lt;/strong&gt; avoid reporting low-level technical details here (use the &lt;code&gt;log&lt;/code&gt; and &lt;code&gt;logError&lt;/code&gt; APIs for that). The initial implementation uses this feature to display command statistics (CPU, memory, etc.) from the new &lt;code&gt;hop&lt;/code&gt; plugin in a clean table via the &lt;code&gt;grafter&lt;/code&gt; plugin.&lt;/p&gt;

&lt;p&gt;With the new &lt;code&gt;hop&lt;/code&gt; plugin, GitRoot now supports 3 languages (Go, TypeScript, Rust) across 4 compilers (Go, TinyGo, Rust, AssemblyScript). Setting up a development environment for this polyglot stack is complex, and keeping it updated is even harder, unless you have the right tools. For this iteration, I have selected &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt; as the default tool for &lt;a href=&quot;blog/version-0.3.0.html#gitroot-bets-on-mise&quot;&gt;managing the GitRoot dev environment&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Beyond technical features, a new focus has been placed on &lt;a href=&quot;blog/version-0.3.0.html#communication-and-community-building&quot;&gt;communication&lt;/a&gt;. You can now follow daily progress on our new fediverse instance (@forge@gitroot.dev), engage in direct chat on matrix (#gitroot:matrix.org), and read a new blog post detailing concrete ways to contribute today.&lt;/p&gt;

&lt;p&gt;Finally, I discuss &lt;a href=&quot;blog/version-0.3.0.html#apex-a-better-web-experience&quot;&gt;Apex&lt;/a&gt; and many &lt;a href=&quot;blog/version-0.3.0.html#and-others-useful-updates&quot;&gt;other tiny but useful things&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;in-depth-details&quot;&gt;In-depth Details&lt;/h2&gt;

&lt;h3 id=&quot;for-admin-activating-an-executor&quot;&gt;For admin: activating an executor&lt;/h3&gt;

&lt;p&gt;When you update GitRoot to &lt;code&gt;0.3.0&lt;/code&gt; a new configuration block will appear in the &lt;code&gt;.gitroot/forgeConfig.yml&lt;/code&gt; file of your &lt;code&gt;root&lt;/code&gt; repository:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;execconf&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;baremetal&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;bwrap&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;user&lt;/span&gt;: nobody
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;uid&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;65534&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;gid&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;65534&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;robind&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;bind&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;container&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;bin&lt;/span&gt;: podman
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;ssh&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;hosts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;user&lt;/span&gt;: myUSer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;address&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;127.0.0.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;port&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;22&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;publickey&lt;/span&gt;: ssh-ed25519 AAAAC3NzaC1...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can enable multiple executors, but GitRoot will only use the first one enabled, checking them in this order: &lt;code&gt;bareMetal&lt;/code&gt;, &lt;code&gt;bwrap&lt;/code&gt;, &lt;code&gt;container&lt;/code&gt; and then &lt;code&gt;ssh&lt;/code&gt;. If you activate an executor, remember to also add the &lt;code&gt;hop&lt;/code&gt; plugin, as it is the only known plugin designed to utilize them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;bare-metal-simplest-but-most-dangerous&quot;&gt;Bare Metal: simplest but most dangerous&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;bareMetal&lt;/code&gt; executor runs commands directly on the host machine. It requires no external dependencies and should work on any hardware. Be extremely cautious: users of your GitRoot instance will gain the ability to execute arbitrary code on the host.&lt;/p&gt;

&lt;h4 id=&quot;bubblewrap-bwrap-lightweight-sandboxing&quot;&gt;Bubblewrap (bwrap): lightweight sandboxing&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;bwrap&lt;/code&gt; executor introduces a lightweight container (sandbox) between executed commands and the host, similar to the technology used by &lt;a target=&quot;_blank&quot; href=&quot;https://flatpak.org/&quot;&gt;flatpack&lt;/a&gt;. To use it, you must install the &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/containers/bubblewrap&quot;&gt;bubblewrap&lt;/a&gt; application.&lt;/p&gt;

&lt;p&gt;By default, GitRoot configures &lt;code&gt;bwrap&lt;/code&gt; to use the &lt;code&gt;nobody&lt;/code&gt; user for restriction, though this can be changed. You can also define bindings between your host and the commands running inside the container. For example, &lt;code&gt;robind: [&amp;quot;/var/log/caddy/access.log&amp;quot;]&lt;/code&gt; would allow a job to execute &lt;code&gt;cat /var/log/caddy/access.log&lt;/code&gt;.&lt;/p&gt;

&lt;h4 id=&quot;container-heavyweight-but-most-flexible&quot;&gt;Container: heavyweight but most flexible&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;container&lt;/code&gt; executor enables the execution of commands within &lt;a target=&quot;_blank&quot; href=&quot;https://podman.io/&quot;&gt;podman&lt;/a&gt; or &lt;a target=&quot;_blank&quot; href=&quot;https://www.docker.com/&quot;&gt;docker&lt;/a&gt; containers, allowing your users to run any OCI image. The only requirement for the administrator is to install either &lt;a target=&quot;_blank&quot; href=&quot;https://podman.io/docs/installation&quot;&gt;podman&lt;/a&gt; or &lt;a target=&quot;_blank&quot; href=&quot;https://www.docker.com/&quot;&gt;docker&lt;/a&gt;. Remember to change the &lt;code&gt;bin&lt;/code&gt; property to &lt;code&gt;docker&lt;/code&gt; if that is your chosen runtime.&lt;/p&gt;

&lt;p&gt;GitRoot includes a useful feature for containers: if the user provides a path to a &lt;code&gt;Containerfile&lt;/code&gt; or &lt;code&gt;Dockerfile&lt;/code&gt;, GitRoot will automatically build the image before running commands inside it.&lt;/p&gt;

&lt;h4 id=&quot;ssh-remote-execution-alpha&quot;&gt;SSH: remote execution (alpha)&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;ssh&lt;/code&gt; executor allows commands to be executed on a remote host via a standard SSH connection. You simply provide the standard SSH connection details. This executor should be considered the most alpha of all options (and yes, all others are also considered alpha).&lt;/p&gt;

&lt;h3 id=&quot;for-user-using-the-hop-plugin&quot;&gt;For user: using the &lt;code&gt;hop&lt;/code&gt; plugin&lt;/h3&gt;

&lt;p&gt;To execute a build (or command execution), users must activate the &lt;code&gt;hop&lt;/code&gt; plugin and configure its execution rules:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;url&lt;/span&gt;: /home/rmaneschi/projects/gitroot/app/testsuite/../plugins/hop/hop-0.0.1.wasm
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: hop
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;version&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;0.0.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;active&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          - &lt;span style=&quot;color:#ff79c6&quot;&gt;command&lt;/span&gt;: cat
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          &lt;span style=&quot;color:#ff79c6&quot;&gt;build&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          &lt;span style=&quot;color:#ff79c6&quot;&gt;cmds&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            - &lt;span style=&quot;color:#ff79c6&quot;&gt;args&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;                - README.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;              &lt;span style=&quot;color:#ff79c6&quot;&gt;cmd&lt;/span&gt;: cat
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          &lt;span style=&quot;color:#ff79c6&quot;&gt;env&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add all desired commands for the plugin to call in &lt;code&gt;run.write.exec&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then in &lt;code&gt;run.configuration.exec&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;set environment variables via &lt;code&gt;env&lt;/code&gt; (e.g., &lt;code&gt;&amp;quot;MY_ENV=my_val&amp;quot;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;specify the &lt;code&gt;build&lt;/code&gt; property (only for the &lt;code&gt;container&lt;/code&gt; executor) to use an existing image (e.g., &lt;code&gt;docker.io/bash:5.3.3&lt;/code&gt;) or to build one from a file (e.g., &lt;code&gt;/my/path/to/Containerfile&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;define the commands and arguments to execute under &lt;code&gt;cmds&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;plugin-api-report-and-reporter&quot;&gt;Plugin api report and reporter&lt;/h3&gt;

&lt;p&gt;Occasionally, plugins need to communicate important information to the user. For instance, the &lt;code&gt;ladybug&lt;/code&gt; plugin might need to report invalid metadata. In traditional forges, a web interface serves this purpose, but GitRoot operates without a fixed UI.&lt;/p&gt;

&lt;p&gt;Past considerations included sending emails (a solution often disliked by developers) or leveraging external communication methods like RSS via the &lt;code&gt;pollen&lt;/code&gt; plugin (introduced in v0.2.0) or the Fediverse via a Go-to-Social instance. As always, GitRoot aims to provide options rather than dictate a single solution.&lt;/p&gt;

&lt;p&gt;To allow reporting without forcing a communication channel, I&amp;rsquo;ve created two new plugin APIs.&lt;/p&gt;

&lt;p&gt;Report: plugins call this API with a severity level (&lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, or &lt;code&gt;error&lt;/code&gt;) and an array of strings to display. GitRoot stacks all reports for every commit and plugin.&lt;/p&gt;

&lt;p&gt;Reporter API: after reports are collected, GitRoot calls all plugins that implement the &lt;code&gt;Reporter&lt;/code&gt; interface.&lt;/p&gt;

&lt;p&gt;Currently, the &lt;code&gt;grafter&lt;/code&gt; plugin is the only known reporter; it adds the collected reports to the graft output. This two new API opens up a wide range of possibilities for future versions. I have several ideas, but I encourage you to &lt;a href=&quot;contact.html&quot;&gt;share yours&lt;/a&gt;!&lt;/p&gt;

&lt;h3 id=&quot;gitroot-bets-on-mise&quot;&gt;GitRoot bets on mise&lt;/h3&gt;

&lt;p&gt;Managing a diverse development environment that spans Go, TypeScript, and Rust can be a significant hurdle. In this iteration, I have standardized on mise as the official tool for managing the GitRoot dev environment. This choice simplifies installation and ensures all dependencies remain correctly updated for development.&lt;/p&gt;

&lt;p&gt;This choice seems to be a good one, as we welcome a new contributor who has already integrated some common tasks using &lt;code&gt;mise&lt;/code&gt;. Thanks to their efforts, we can now simply run &lt;code&gt;mise run build&lt;/code&gt; to compile GitRoot or &lt;code&gt;mise run test&lt;/code&gt; to execute all tests.&lt;/p&gt;

&lt;h3 id=&quot;communication-and-community-building&quot;&gt;Communication and community building&lt;/h3&gt;

&lt;p&gt;Alongside the technical work for v0.3.0, a focused effort was put into non-technical tasks to improve communication with the community.&lt;/p&gt;

&lt;p&gt;To allow you to follow daily progress and current thoughts, I have set up a &lt;a target=&quot;_blank&quot; href=&quot;https://gotosocial.org/&quot;&gt;GoToSocial&lt;/a&gt; (Mastodon) instance. You can find the official account at &lt;code&gt;@forge@gitroot.dev&lt;/code&gt; or directly at &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev&quot;&gt;https://gts.gitroot.dev&lt;/a&gt;. This is the best place for real-time updates.&lt;/p&gt;

&lt;p&gt;I also established a direct chat room on &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.org/&quot;&gt;matrix&lt;/a&gt;, accessible via &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/#gitroot:matrix.org&quot;&gt;https://matrix.to/#/#gitroot:matrix.org&lt;/a&gt;. Feel free to stop by to ask questions or just say hello.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ve added a &lt;a href=&quot;contact.html&quot;&gt;contact&lt;/a&gt; page to ensure you can easily find a way to reach me. It includes multiple options such as email, mastodon, or matrix for direct chat. Please don&amp;rsquo;t hesitate to come say &amp;ldquo;hi&amp;rdquo;!&lt;/p&gt;

&lt;p&gt;Finally, I published a blog post titled &lt;a href=&quot;blog/how-to-help_en_11-2025.html&quot;&gt;&amp;ldquo;How to help building GitRoot 11-2025&amp;rdquo;&lt;/a&gt;. The purpose of this post is to present you with concrete, immediate contribution opportunities, rather than speculating on future possibilities. If you wish to get involved, this is the place to start.&lt;/p&gt;

&lt;h3 id=&quot;apex-a-better-web-experience&quot;&gt;Apex a better web experience&lt;/h3&gt;

&lt;p&gt;The Apex plugin receives numerous bug fixes and quality of life improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;favicon support: a favicon can now be added via the configuration&lt;/li&gt;
&lt;li&gt;styled 404 page: the 404 error page now correctly uses the site&amp;rsquo;s CSS&lt;/li&gt;
&lt;li&gt;HTML metadata configuration: I have added configuration options for HTML metadata, which is useful for SEO and for including specific tags like &lt;code&gt;og&lt;/code&gt; (Open Graph) or &lt;code&gt;fediverse&lt;/code&gt; metadata&lt;/li&gt;
&lt;li&gt;menu highlighting: the current page in the navigation menu is now highlighted&lt;/li&gt;
&lt;li&gt;lightweight template: added some variables in header and footer templates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Breaking:&lt;/strong&gt; previously, the &lt;code&gt;apex&lt;/code&gt; plugin rendered markdown metadata as small blocks at the top of the page by default. With this version, it will no longer render this metadata unless the markdown page explicitly includes &lt;code&gt;apexRenderMetadatas: true&lt;/code&gt; in its front matter.&lt;/p&gt;

&lt;h3 id=&quot;and-others-useful-updates&quot;&gt;And others useful updates&lt;/h3&gt;

&lt;p&gt;Although this article is already quite long, I want to briefly mention several smaller yet important tasks completed in this release.&lt;/p&gt;

&lt;h4 id=&quot;forgeconf-update&quot;&gt;ForgeConf update&lt;/h4&gt;

&lt;p&gt;The parameters &lt;code&gt;externalsshaddr&lt;/code&gt; (default value &lt;code&gt;ssh://localhost:4545/&lt;/code&gt;) and &lt;code&gt;externalhttpaddr&lt;/code&gt; (default value &lt;code&gt;http://localhost:4546/&lt;/code&gt;) have been added to ensure the display of correct external urls to users. These addresses are notably used by the &lt;code&gt;apex&lt;/code&gt; plugin generate proper public links. They are kept separate from the internal &lt;code&gt;sshaddr&lt;/code&gt; and &lt;code&gt;httpaddr&lt;/code&gt; because your GitRoot instance might be configured to listen internally (on &lt;code&gt;127.0.0.1&lt;/code&gt;) while being reachable externally through a reverse proxy.&lt;/p&gt;

&lt;h4 id=&quot;integrated-updater&quot;&gt;Integrated updater&lt;/h4&gt;

&lt;p&gt;I&amp;rsquo;ve added a preliminary implementation for an integrated updater. This feature will handle breaking changes gracefully. For this release, I&amp;rsquo;ve added some new parameters to &lt;code&gt;forgeConf.yml&lt;/code&gt;, the integrated updater will handle these by setting standard values that should not affect your current user instance setup.&lt;/p&gt;

&lt;h4 id=&quot;rust-and-wasm&quot;&gt;Rust and WASM&lt;/h4&gt;

&lt;p&gt;While integrating Rust, I discovered that the WASM (and specifically WASI Preview 1) target does not permit exporting the standard &lt;code&gt;free&lt;/code&gt; and &lt;code&gt;malloc&lt;/code&gt; functions, which GitRoot requires. I&amp;rsquo;ve added an option to rename these functions to &lt;code&gt;gitrootAlloc&lt;/code&gt; and &lt;code&gt;gitrootFree&lt;/code&gt;, resolving this compatibility issue.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.3.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.2.0.md</link><title>0.2.0</title><pubDate>2025/09/18</pubDate><description>&lt;h1 id=&quot;version-0-2-0-is-here&quot;&gt;Version 0.2.0 is here&lt;/h1&gt;

&lt;p&gt;In this version, my main focus was on stabilization. I fixed over 14 issues, some of which were identified before version 0.1.0, but a significant proportion were reported by YOU. A big thanks to everyone who took part of it in any way.&lt;/p&gt;

&lt;p&gt;Every time I shared gitroot, I came across new issues with the documentation and binaries, but also questions such as &amp;ldquo;how can I compile it?&amp;rdquo; or &amp;ldquo;what&amp;rsquo;s the next step?&amp;rdquo;. And I&amp;rsquo;m delighted to see your enthusiasm for it!&lt;/p&gt;

&lt;h2 id=&quot;first-contribution&quot;&gt;First contribution&lt;/h2&gt;

&lt;p&gt;Although all my new contributions were related to &amp;ldquo;bug fixes&amp;rdquo;, some of them required new features.&lt;/p&gt;

&lt;h3 id=&quot;no-notification-no-contribution&quot;&gt;No notification == no contribution&lt;/h3&gt;

&lt;p&gt;The first amusing problem came from the first contributor! Fifteen days later, he contacted me to say, &amp;ldquo;Hi, I pushed a branch where I corrected a lot of typos, but&amp;hellip; nothing has happened since I pushed it?&amp;rdquo;. In fact, I wasn&amp;rsquo;t actually aware that a new branch had been pushed. I had forgotten to implement a mechanism that would notify me whenever a new contribution is published!&lt;/p&gt;

&lt;p&gt;A simple way to achieve this is to add an RSS feed (yes, I was inspired by Radicle for this feature). This is ideal, because all I have to do is add an &amp;ldquo;rss.xml&amp;rdquo; file to the web folder&amp;hellip;and the current API of plugin 0.1.0 allows me to do this! So I created the &lt;code&gt;pollen&lt;/code&gt; plugin. It takes all the commits from all branches and adds an &lt;code&gt;rss:item&lt;/code&gt; to a page.&lt;/p&gt;

&lt;p&gt;However, in order to achieve this, I opted to experiment with a different programming language (all the plugins are developed in Golang). I chose &lt;code&gt;assemblyscript&lt;/code&gt;, which is a subset of the &lt;code&gt;typescript&lt;/code&gt; language. So I added a new &lt;code&gt;plugin sdk&lt;/code&gt; in typescript.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To follow all gitroot commits follow &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/rss/all-commits.xml&quot;&gt;https://gitroot.dev/rss/all-commits.xml&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;lot-of-bugs&quot;&gt;Lot of bugs&lt;/h3&gt;

&lt;p&gt;During this first contribution, I discovered numerous bugs in the “grafter” plugin. From the &lt;a href=&quot;issues/close/v0.2.0/73e8-graft-diff-not-working.html&quot;&gt;diff rendering&lt;/a&gt; to &lt;a href=&quot;issues/close/v0.2.0/e188-graft-merge-not-wanted.html&quot;&gt;unwanted merging&lt;/a&gt; and &lt;a href=&quot;issues/close/9422-graft-need-3-review.html&quot;&gt;very complicated final merging&lt;/a&gt;, nothing worked! Even the basic GitRoot features were painful to use :D&lt;/p&gt;

&lt;h2 id=&quot;let-s-the-show-continue&quot;&gt;Let&amp;rsquo;s the show continue&lt;/h2&gt;

&lt;h3 id=&quot;no-linux-no-gitroot&quot;&gt;No linux == no gitroot&lt;/h3&gt;

&lt;p&gt;Surprisingly, someone wanted to compile gitroot on a Mac. Well, gitroot is actually written in Go, so there&amp;rsquo;s no problem doing that&amp;hellip; Except that I forgot to publish a fix I made to a dependency. Go couldn&amp;rsquo;t download the right dependency and therefore fails to compile completely&amp;hellip; But everything is OK now! I added an automatic compilation for Mac, you can find this on the &lt;a href=&quot;CHANGELOG.html&quot;&gt;version&lt;/a&gt; page.&lt;/p&gt;

&lt;h3 id=&quot;doc-doc-doc&quot;&gt;Doc, doc&amp;hellip; doc?&lt;/h3&gt;

&lt;p&gt;After that, someone tried to run gitroot on their local computer, as described in the &lt;a href=&quot;doc/tutorials/from_zero_to_hero.html&quot;&gt;document&lt;/a&gt;. However, when version &lt;code&gt;0.1.0&lt;/code&gt; was released, I discovered a bug. Gitroot cannot know where the &lt;code&gt;root&lt;/code&gt; repository is located (because you can change its name). To fix this, I added a configuration file before initialization, which had to be passed on launch&amp;hellip; It&amp;rsquo;s my fault, I forgot to update this tutorial.&lt;/p&gt;

&lt;h3 id=&quot;a-lot-more&quot;&gt;A lot more&lt;/h3&gt;

&lt;p&gt;As you can see, there were many mistakes in the first version. However, they have all been fixed, and, of course, I have added plenty more to keep me busy for the next version!&lt;/p&gt;

&lt;h3 id=&quot;more-technicals-bugs&quot;&gt;More technicals bugs&lt;/h3&gt;

&lt;p&gt;But I also fixed several important issues that were the result of some shortcuts that every project has to take for publication. One was about a &lt;a href=&quot;issues/close/v0.2.0/95b0-plugins-fs-bug.html&quot;&gt;FS bug&lt;/a&gt; when directories were empty, another concerned the &lt;a href=&quot;issues/close/v0.2.0/f1ab-garbage-collecte-plugins.html&quot;&gt;garbage collect plugin&lt;/a&gt; when the plugin had finished running, and many others.&lt;/p&gt;

&lt;h2 id=&quot;simplify-plugin-install&quot;&gt;Simplify plugin install&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;0.1.0&lt;/code&gt; version, to install a plugin you should download it, tell to gitroot where it is and then after push/pull activate/configure it. Now it is way more simple, just give an url, like &lt;code&gt;https://gitroot.dev/releases/0.2.0/pollen-0.0.1.wasm&lt;/code&gt; and optionnally (but encouraged) a checksum &lt;code&gt;sha256: 49cd8e1846b1612a0d5686918b502fe29e070e7bf9613c1ba9a1dfe5417d0f2a&lt;/code&gt;. Gitroot will download, check and run it. Of course you need to activate it. All url and checksum are in the &lt;a href=&quot;CHANGELOG.html&quot;&gt;versions page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have added a mecanisme to check that the plugin you try to run is compatble with your version of GitRoot. So launching &lt;code&gt;ladybug-0.0.1.wasm&lt;/code&gt; in &lt;code&gt;gitroot-0.2.0&lt;/code&gt; will log an error and not run the wasm. As GitRoot is in alpha version all new gitroot version will need plugins update.&lt;/p&gt;

&lt;h2 id=&quot;what-next&quot;&gt;What next?&lt;/h2&gt;

&lt;p&gt;With the exception of any new bugs detected for the next iteration, I would like to focus on the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;creating a plugins marketplace&lt;/li&gt;
&lt;li&gt;adding a new language SDK for plugins (Maybe Rust? Or Zig?)&lt;/li&gt;
&lt;li&gt;rebasing the Git lib fork and applying patches&lt;/li&gt;
&lt;/ul&gt;
</description><guid>https://gitroot.dev//blog/version-0.2.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.1.0.md</link><title>0.1.0</title><pubDate>2025/09/18</pubDate><description>&lt;h1 id=&quot;gitroot-0-1-0-is-deployed&quot;&gt;GitRoot 0.1.0 is deployed 🎉��&lt;/h1&gt;

&lt;h2 id=&quot;gitroot-in-numbers&quot;&gt;GitRoot in numbers:&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;58 commits&lt;/li&gt;
&lt;li&gt;+500 files &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/&quot;&gt;https://gitroot.dev/worktree/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;1 issue in gitlab (closed) &lt;a target=&quot;_blank&quot; href=&quot;https://gitlab.com/gitroot/gitroot/-/issues/1&quot;&gt;https://gitlab.com/gitroot/gitroot/-/issues/1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;29 issues close &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/boards/closed.html&quot;&gt;https://gitroot.dev/boards/closed.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;43 issues open &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/boards/issues.html&quot;&gt;https://gitroot.dev/boards/issues.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;22 pages de doc &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/doc/&quot;&gt;https://gitroot.dev/doc/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;4 licences &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/README.html#license&quot;&gt;https://gitroot.dev/README.html#license&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;4 plugins &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/CHANGELOG.html#plugins&quot;&gt;https://gitroot.dev/CHANGELOG.html#plugins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But if you retain only one, it is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 contributor &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/.gitroot/users.yml.html&quot;&gt;https://gitroot.dev/worktree/.gitroot/users.yml.html&lt;/a&gt; (others are forge and plugins 😭)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So there is only one thig to do&amp;hellip; read the doc and contribute!&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.1.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/security-vulnerability-1.md</link><title>blog/security-vulnerability-1.md</title><pubDate>2026/03/30</pubDate><description>&lt;h1 id=&quot;security-advisory-executor-command-injection&quot;&gt;Security Advisory: Executor Command Injection&lt;/h1&gt;

&lt;h2 id=&quot;1-executive-summary&quot;&gt;1. Executive Summary&lt;/h2&gt;

&lt;p&gt;A vulnerability has been identified within the GitRoot &lt;strong&gt;executors&lt;/strong&gt; system. Under specific conditions, it is possible for a malicious actor to execute unauthorized commands on the host machine. Given the complexity of the required conditions, this vulnerability is not easily exploitable in a standard production environment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Severity:&lt;/strong&gt; Moderate&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Affected Version:&lt;/strong&gt; 0.3.0&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fix Status:&lt;/strong&gt; Scheduled for version 0.4.0&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;2-technical-details&quot;&gt;2. Technical Details&lt;/h2&gt;

&lt;p&gt;The vulnerability stems from the way executors validate commands defined by plugins. Currently, a plugin can declare a benign command in its manifest (e.g., &lt;code&gt;cat&lt;/code&gt;), but execute an arbitrary command during runtime (e.g., &lt;code&gt;rm -rf data/&lt;/code&gt;). GitRoot currently lacks a strict enforcement mechanism to verify that the executed binary matches the declared command.&lt;/p&gt;

&lt;h2 id=&quot;3-impact-analysis&quot;&gt;3. Impact Analysis&lt;/h2&gt;

&lt;p&gt;The level of risk depends on your infrastructure configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;High Risk:&lt;/strong&gt; Users utilizing &lt;code&gt;bareMetal&lt;/code&gt; or &lt;code&gt;ssh&lt;/code&gt; executors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Low Risk:&lt;/strong&gt; Users utilizing &lt;code&gt;bubblewrap&lt;/code&gt; or &lt;code&gt;container&lt;/code&gt; isolation (as the sandbox significantly limits the potential impact on the host).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exploitability Constraints:&lt;/strong&gt;
This vulnerability is not a &amp;ldquo;remote code execution&amp;rdquo; flaw in the traditional sense. Exploitation requires:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Installing a malicious plugin.&lt;/li&gt;
&lt;li&gt;Having write access to the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt; of the root repository.&lt;/li&gt;
&lt;li&gt;Merging a malicious &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt; into the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Currently, creating plugins is undocumented, and the only known plugin using executors is &lt;code&gt;hop&lt;/code&gt;. Since any modification to &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt; requires a merge into the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt;, a standard code review process should easily detect and prevent such malicious entries.&lt;/p&gt;

&lt;h2 id=&quot;4-immediate-mitigations&quot;&gt;4. Immediate Mitigations&lt;/h2&gt;

&lt;p&gt;If you cannot wait for the 0.4.0 release, we recommend applying the following measures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Disable Executors:&lt;/strong&gt; Set your forge configuration for executors to &lt;code&gt;None&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secure Executors:&lt;/strong&gt; Use more secure executors, such as &lt;code&gt;bwrap&lt;/code&gt; or &lt;code&gt;container&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plugin Trust:&lt;/strong&gt; Only install and enable plugins from trusted sources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access Control:&lt;/strong&gt; Strictly monitor and review all changes to &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt;, specifically focusing on authorized system commands.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;5-patch-schedule&quot;&gt;5. Patch Schedule&lt;/h2&gt;

&lt;p&gt;This vulnerability will be fully addressed in &lt;strong&gt;version 0.4.0&lt;/strong&gt;.
We have decided not to release an intermediate 0.3.1 patch, as the attack vector relies on an &amp;ldquo;insider&amp;rdquo; or &amp;ldquo;compromised account&amp;rdquo; scenario that is effectively mitigated by standard code review practices.&lt;/p&gt;

&lt;h2 id=&quot;6-security-contact&quot;&gt;6. Security Contact&lt;/h2&gt;

&lt;p&gt;Transparency and security are core values at GitRoot. If you discover any other potential vulnerabilities or have questions regarding this report, please contact us directly at &lt;a href=&quot;blog/contact@gitroot.dev&quot;&gt;contact@gitroot.dev&lt;/a&gt;.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/security-vulnerability-1.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/modifying-API-SDK-plugin.md</link><title>Modifying the API in the SDK Plugin</title><pubDate>2026/04/06</pubDate><description>&lt;h1 id=&quot;modifying-the-api-in-the-sdk-plugin&quot;&gt;Modifying the API in the SDK Plugin&lt;/h1&gt;

&lt;p&gt;The last feature for version 0.4 is about adding &lt;code&gt;mv&lt;/code&gt; (move) and &lt;code&gt;cp&lt;/code&gt; (copy) functions to the SDK plugin. I&amp;rsquo;m taking advantage of this development to explain how the plumbing works under the hood.&lt;/p&gt;

&lt;p&gt;Right now, the SDK plugin is undocumented because there are a lot of breaking changes coming. Every time I develop a new plugin, I discover better ways to do things, and the SDK needs to adapt.&lt;/p&gt;

&lt;p&gt;For example, when I added &lt;code&gt;apex_code&lt;/code&gt; (a plugin that takes code as a string and returns syntax-highlighted HTML), I wondered: &amp;ldquo;Is this a standalone plugin?&amp;rdquo;. Not really, because there&amp;rsquo;s no file to manipulate, neither in input nor output. Instead, it manages blocks inside an existing file. The SDK wasn&amp;rsquo;t originally built with that use case in mind.&lt;/p&gt;

&lt;p&gt;Anyway, I think explaining how the SDK works through a concrete example will be helpful, if not for you, at least for me.&lt;/p&gt;

&lt;h2 id=&quot;what-is-the-sdk-s-job&quot;&gt;What is the SDK&amp;rsquo;s job?&lt;/h2&gt;

&lt;p&gt;The SDK exists to pass data from GitRoot (the host) to the plugin (the guest). As you know, GitRoot uses WebAssembly (WASM) to manage plugins. Unfortunately, WASM is still a bit low-level to implement: between the host and the guest, only integers can travel.&lt;/p&gt;

&lt;p&gt;Wait, only integers?&lt;/p&gt;

&lt;p&gt;Yes. So, how do we send a string? The host can store any kind of data in the guest&amp;rsquo;s memory. GitRoot stores a string, retrieves the pointer (the memory address) and the size, and sends those two integers to the guest. The guest then retrieves the string from its own memory. It&amp;rsquo;s a complex dance just for a simple string. The SDK is here to hide all that mess.&lt;/p&gt;

&lt;p&gt;When you build a plugin, you just implement an interface. The SDK handles the rest: you receive objects and strings, and you send back data without ever worrying about the underlying WASM pointers.&lt;/p&gt;

&lt;h2 id=&quot;what-does-a-concrete-implementation-look-like&quot;&gt;What does a concrete implementation look like?&lt;/h2&gt;

&lt;p&gt;When I add a new function, I always start with the host side. All functions are located in &lt;a href=&quot;app/server/plugin/runtime.go&quot;&gt;runtime.go&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We need to register the function in &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/wazero/wazero&quot;&gt;wazero&lt;/a&gt;, our WASM runtime. I add two versions because I haven&amp;rsquo;t found a clean way to bridge the difference between AssemblyScript and Go/Rust yet. Hopefully, I&amp;rsquo;ll figure that out one day.&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;r.wazRun.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#50fa7b&quot;&gt;NewFunctionBuilder&lt;/span&gt;().&lt;span style=&quot;color:#50fa7b&quot;&gt;WithFunc&lt;/span&gt;(r.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(&lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;)).&lt;span style=&quot;color:#50fa7b&quot;&gt;Export&lt;/span&gt;(&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;moveFile&amp;#34;&lt;/span&gt;).
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#50fa7b&quot;&gt;NewFunctionBuilder&lt;/span&gt;().&lt;span style=&quot;color:#50fa7b&quot;&gt;WithFunc&lt;/span&gt;(r.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(&lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;)).&lt;span style=&quot;color:#50fa7b&quot;&gt;Export&lt;/span&gt;(&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;moveFileAS&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I implement the builder:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(forAS &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;interface&lt;/span&gt;{} {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; !forAS {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, ptr, size, ptr2, size2 &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#6272a4&quot;&gt;// from ptr/size to string or object
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;			r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromPath, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	} &lt;span style=&quot;color:#ff79c6&quot;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, ptr, ptr2 &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#6272a4&quot;&gt;// from ptr to string or object
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;			r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromPath, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Yeah, I know. As a Gopher, seeing &lt;code&gt;interface{}&lt;/code&gt; as a return type and all these uint32 feels wrong. But hey, we&amp;rsquo;re not here for a code review, we&amp;rsquo;re here to add an API. If you have a cleaner way, feel free to submit a graft!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We want to allow any plugin to move a file. Let&amp;rsquo;s define the internal &lt;code&gt;moveFile&lt;/code&gt; function:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromPath, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#6272a4&quot;&gt;//need to do something...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Actually, there&amp;rsquo;s a catch, GitRoot uses three internal filesystems: one for the git worktree, one for web content, and one for the cache. Even if they are abstracted under one root, the code often expects a specific FS. I&amp;rsquo;ll probably clean this up later, but for now:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#6272a4&quot;&gt;//need to do something...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now your plugin can call &lt;code&gt;moveFile(&amp;quot;cache&amp;quot;, &amp;quot;artifacts/myBin&amp;quot;, &amp;quot;web&amp;quot;, &amp;quot;releases/myBin-v1.0&amp;quot;)&lt;/code&gt; to publish an artifact, or &lt;code&gt;moveFile(&amp;quot;web&amp;quot;, &amp;quot;index.html&amp;quot;, &amp;quot;web&amp;quot;, &amp;quot;old/index.html&amp;quot;)&lt;/code&gt; to back up a file.&lt;/p&gt;

&lt;p&gt;I won&amp;rsquo;t bore you with the implementation logic here. If you&amp;rsquo;re curious, check the &lt;a href=&quot;app/server/plugin/runtime.go&quot;&gt;code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is how the final builder looks, converting pointers to strings:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(forAS &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;interface&lt;/span&gt;{} {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; !forAS {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, fromFsPtr, fromFsSize, fromPathPtr, fromGlobPathSize, toFsPtr, toFsSize, toPathPtr, toPathSize &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;uint64&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile fromFs&amp;#34;&lt;/span&gt;, fromFsPtr, fromFsSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile fromGlobPath&amp;#34;&lt;/span&gt;, fromPathPtr, fromGlobPathSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile toFs&amp;#34;&lt;/span&gt;, toFsPtr, toFsSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile toPath&amp;#34;&lt;/span&gt;, toPathPtr, toPathSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	} &lt;span style=&quot;color:#ff79c6&quot;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, fromFsPtr, fromPathPtr, toFsPtr, toPathPtr &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;uint64&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS fromFs&amp;#34;&lt;/span&gt;, fromFsPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS fromGlobPath&amp;#34;&lt;/span&gt;, fromPathPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS toFs&amp;#34;&lt;/span&gt;, toFsPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS toPath&amp;#34;&lt;/span&gt;, toPathPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L38&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L38&quot;&gt;38&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L39&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L39&quot;&gt;39&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L40&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L40&quot;&gt;40&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L41&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L41&quot;&gt;41&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L42&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L42&quot;&gt;42&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L43&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L43&quot;&gt;43&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When I need to pass complex objects, I use &lt;code&gt;json.Marshal&lt;/code&gt; in GitRoot and &lt;code&gt;json.Unmarshal&lt;/code&gt; in the SDK. It works, but don&amp;rsquo;t get too attached to it, I might change the serialization method in the future.&lt;/p&gt;

&lt;h2 id=&quot;inside-the-sdk&quot;&gt;Inside the SDK&lt;/h2&gt;

&lt;p&gt;Now that the host is ready, we need to implement the other side: the SDK itself. I usually start with Go, then move to Rust and AssemblyScript. Sometimes I forget the others&amp;hellip; shame on me! I&amp;rsquo;m planning to automate this with some boilerplate generation later.&lt;/p&gt;

&lt;p&gt;In the &lt;a href=&quot;app/libs/golang/plugin/model/server.go&quot;&gt;server&lt;/a&gt; interface:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;type&lt;/span&gt; Server &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;interface&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#6272a4&quot;&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;    &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#6272a4&quot;&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make it more &amp;ldquo;user-friendly&amp;rdquo;, I use a struct to represent the filesystem in &lt;a href=&quot;app/libs/golang/plugin/model/fs.go&quot;&gt;model/fs.go&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (g &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;GrFs) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(fromPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; g.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(g.base, fromPath, g.base, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (g &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;GrFs) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileToFs&lt;/span&gt;(fromPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, toFs &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;GrFs, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; g.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(g.base, fromPath, toFs.base, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The actual WASM call happens in &lt;a href=&quot;app/libs/golang/plugin/server.go&quot;&gt;plugin/server.go&lt;/a&gt;, where we transform strings back into pointers:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;// MoveFile implements [model.ServerNeeded].
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (s &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;pServer) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(fromFs model.FsBase, fromPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, toFs model.FsBase, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr, size &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;string&lt;/span&gt;(fromFs))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr2, size2 &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(fromPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr3, size3 &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;string&lt;/span&gt;(toFs))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr4, size4 &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	resPtrSize &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;_moveFile&lt;/span&gt;(ptr, size, ptr2, size2, ptr3, size3, ptr4, size4)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(fromFs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(fromPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(toFs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;ptrSizeToError&lt;/span&gt;(resPtrSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally, the link to the host via &lt;code&gt;go:wasmimport&lt;/code&gt; in &lt;a href=&quot;app/libs/golang/plugin/imports.go&quot;&gt;plugin/imports.go&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;//go:wasmimport gitroot moveFile
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;_moveFile&lt;/span&gt;(fromFsPtr, fromFsSize, fromPathPtr, fromPathSize, toFsPtr, toFsSize, toPathPtr, toPathSize &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) (ptrSize &lt;span style=&quot;color:#8be9fd&quot;&gt;uint64&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;how-to-use-it&quot;&gt;How to use it?&lt;/h2&gt;

&lt;p&gt;Now, any plugin can use this. Here&amp;rsquo;s a simple Go plugin that triggers a build whenever a &lt;code&gt;.go&lt;/code&gt; file is modified and moves the binary to the web folder.&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;package&lt;/span&gt; main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;path/filepath&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;strings&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	gitroot &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;gitroot.dev/libs/golang/plugin&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;gitroot.dev/libs/golang/plugin/model&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;var&lt;/span&gt; defaultRun = []model.PluginRun{{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	Path:   &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*.go&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	When:   model.PluginRunWhenAll,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	Branch: []&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;{&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	Write: model.PluginWrite{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		Web: []model.PluginWriteRight{{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			Path: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;releases/main&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			Can:  model.PluginWriteRightCanAll,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;type&lt;/span&gt; Plugin &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	server model.Server
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    needToBuild &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;Init&lt;/span&gt;(repoName &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, confHasChanged &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;, serializedConf &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;StartCommit&lt;/span&gt;(commit model.Commit) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                  { &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;AddFile&lt;/span&gt;(file model.File) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L38&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L38&quot;&gt;38&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L39&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L39&quot;&gt;39&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L40&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L40&quot;&gt;40&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L41&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L41&quot;&gt;41&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;ModFile&lt;/span&gt;(file model.File) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L42&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L42&quot;&gt;42&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L43&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L43&quot;&gt;43&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L44&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L44&quot;&gt;44&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L45&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L45&quot;&gt;45&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L46&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L46&quot;&gt;46&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;DelFile&lt;/span&gt;(file model.File) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L47&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L47&quot;&gt;47&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L48&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L48&quot;&gt;48&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L49&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L49&quot;&gt;49&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L50&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L50&quot;&gt;50&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L51&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L51&quot;&gt;51&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;EndCommit&lt;/span&gt;(commit model.Commit) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                    { &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L52&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L52&quot;&gt;52&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L53&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L53&quot;&gt;53&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;Finish&lt;/span&gt;() &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L54&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L54&quot;&gt;54&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; p.needToBuild {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L55&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L55&quot;&gt;55&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        res, _ &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; p.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;Exec&lt;/span&gt;(model.Exec{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L56&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L56&quot;&gt;56&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            Cmds: []model.Cmd{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L57&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L57&quot;&gt;57&lt;/a&gt;&lt;/span&gt;&lt;span&gt;                {Cmd: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;go&amp;#34;&lt;/span&gt;, Args: []&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;{&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;build&amp;#34;&lt;/span&gt;}},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L58&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L58&quot;&gt;58&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L59&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L59&quot;&gt;59&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            Artifacts: []&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;{&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L60&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L60&quot;&gt;60&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L61&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L61&quot;&gt;61&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; p.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;Cache&lt;/span&gt;().&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileToFs&lt;/span&gt;(res.Artifacts[&lt;span style=&quot;color:#bd93f9&quot;&gt;0&lt;/span&gt;], p.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;Webcontent&lt;/span&gt;(), filepath.&lt;span style=&quot;color:#50fa7b&quot;&gt;Join&lt;/span&gt;(&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;releases&amp;#34;&lt;/span&gt;, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L62&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L62&quot;&gt;62&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L63&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L63&quot;&gt;63&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L64&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L64&quot;&gt;64&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L65&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L65&quot;&gt;65&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L66&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L66&quot;&gt;66&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;Build&lt;/span&gt;(server model.Server) model.Plugin {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L67&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L67&quot;&gt;67&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;&amp;amp;&lt;/span&gt;Plugin{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L68&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L68&quot;&gt;68&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		server: server,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L69&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L69&quot;&gt;69&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L70&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L70&quot;&gt;70&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L71&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L71&quot;&gt;71&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L72&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L72&quot;&gt;72&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;//go:wasmexport install
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L73&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L73&quot;&gt;73&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L74&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L74&quot;&gt;74&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	gitroot.&lt;span style=&quot;color:#50fa7b&quot;&gt;Register&lt;/span&gt;(defaultRun, Build)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L75&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L75&quot;&gt;75&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;is-that-all&quot;&gt;Is that all?&lt;/h2&gt;

&lt;p&gt;Nope. I still need to update the &lt;a href=&quot;app/libs/rust/plugin/src/fs.rs&quot;&gt;Rust&lt;/a&gt; and &lt;a href=&quot;app/libs/ts/plugin/fs.ts&quot;&gt;AssemblyScript&lt;/a&gt; SDKs and, of course, add &lt;a href=&quot;app/libs/golang/plugin/test/fakeserver.go&quot;&gt;tests&lt;/a&gt;. But this should give you a good idea of how the plumbing works.&lt;/p&gt;

&lt;p&gt;If anything is unclear, &lt;a href=&quot;contact.html&quot;&gt;let me know&lt;/a&gt;!&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/modifying-API-SDK-plugin.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/how-to-help_fr_11-2025.md</link><title>Comment contribuer au développement de GitRoot</title><pubDate>2025/11/18</pubDate><description>&lt;h1 id=&quot;comment-contribuer-au-développement-de-gitroot-11-2025&quot;&gt;Comment contribuer au développement de GitRoot 11-2025&lt;/h1&gt;

&lt;p&gt;Je viens tout juste de lire &lt;a target=&quot;_blank&quot; href=&quot;https://antonin.delpeuch.eu/posts/too-much-time-on-your-hands/&quot;&gt;cette article&lt;/a&gt; et je n&amp;rsquo;arrête pas d&amp;rsquo;y penser. Je le met en perspective des contributeurs de GitRoot. Je suis le seul contributeur (à part mon ami Damien qui a corrigé de nombreuses fautes, merci à lui) sur ce projet. Et comme le montre ce &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge/statuses/01K98BCFEB9V430JD9TFQ32YZN&quot;&gt;toot&lt;/a&gt; j&amp;rsquo;ai un travail pour payer les factures, 1 femme et 2 enfants qui m&amp;rsquo;occupent pratiquement tout le reste du temps. À ça je rajoute GitRoot, pour le moment je m&amp;rsquo;éclate et je tiens bon mais pour combien de temps je vais y arriver ?&lt;/p&gt;

&lt;p&gt;Alors pourquoi pas de contributeurs externes sur GitRoot ?&lt;/p&gt;

&lt;p&gt;Je me dit souvent que le projet est jeune, trop jeune, pour que ça soit accessible à un developpeur externe. Parfois je me dit qu&amp;rsquo;un meilleurs système de ticket serait nécessaire pour avoir un label &amp;ldquo;welcome new contribution&amp;rdquo;. Parfois je me dit qu&amp;rsquo;une fois que j&amp;rsquo;aurais déployé les plugins sdk et fait un market place de plugins, enfin vous pourrez venir m&amp;rsquo;aider à faire que GitRoot soit un vrai projet open-source.&lt;/p&gt;

&lt;p&gt;Mais, car il y a un mais, cette liste ne s&amp;rsquo;arrêtera jamais. Soyons franc il y aura toujours un truc de plus à faire avant que vous puissiez m&amp;rsquo;aider à faire que GitRoot soit au top. J&amp;rsquo;ai donc décidé de faire la chose la plus simple à l&amp;rsquo;heure actuelle pour vous expliquer comment m&amp;rsquo;aider: un article de blog.&lt;/p&gt;

&lt;p&gt;Ici, je vais vous expliquer tout ce qu&amp;rsquo;il est possible de faire en l&amp;rsquo;état actuel du projet. Pas ce qu&amp;rsquo;il sera possible de faire un jour &lt;em&gt;peu être&lt;/em&gt;, mais ce que vous pouvez faire là maintenant. Je réitèrerais ce genre d&amp;rsquo;article tant que nécessaire.&lt;/p&gt;

&lt;h2 id=&quot;pré-requis&quot;&gt;Pré-requis&lt;/h2&gt;

&lt;p&gt;Il n&amp;rsquo;y a pas grand chose à faire pour être prêt à aider sur GitRoot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;vous devez avoir &lt;a target=&quot;_blank&quot; href=&quot;https://git-scm.com/&quot;&gt;git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;git clone ssh://gitroot.dev&lt;/li&gt;
&lt;li&gt;cd gitroot.dev&lt;/li&gt;
&lt;li&gt;./gitroot/init.sh&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si vous souhaitez coder il vous faudra en plus:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/bash/manual/bash.html&quot;&gt;bash&lt;/a&gt; et &lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/make/&quot;&gt;make&lt;/a&gt; (si vous êtes dev il y a de grandes chances que vous les aillez déjà)&lt;/li&gt;
&lt;li&gt;installer &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt;, c&amp;rsquo;est le plus simple pour avoir tous les compilateurs dans les bonnes versions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Et voila vous êtes bon pour commencer à aider. Mais que faire ? Et bien comme tout bon developpeur je vais vous répondre &amp;ldquo;ça dépend&amp;rdquo;. Ça dépend de ce que vous voulez faire ! Je vais lister dessous un ensemble de taches possibles, vous choisissez.&lt;/p&gt;

&lt;h2 id=&quot;avant-de-commencer&quot;&gt;Avant de commencer&lt;/h2&gt;

&lt;p&gt;Avant de vous dire ce que vous pouvez faire, je me dit qu&amp;rsquo;il serait bien de vous expliquer ce que moi je fais. Donc pour cette version 0.3.0 ma priorité absolue est de faire en sorte que GitRoot build GitRoot lui même. Aujourd&amp;rsquo;hui à chaque release je fais les binaires sur ma macine et je les scp. Je veux qu&amp;rsquo;avec cette version 0.3.0 cela ne soit plus le cas.&lt;/p&gt;

&lt;p&gt;Donc, si j&amp;rsquo;arrive à me retenir, je ne devrais pas faire les tickets qui suivent. Vous avez tout le temps dont vous souhaitez pour les faire. Le plus simple est, je pense, de nous retrouver sur &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;matrix channel gitroot&lt;/a&gt; pour en discutter.&lt;/p&gt;

&lt;p&gt;On peut également en parler par mail &lt;a href=&quot;blog/contact@gitroot.dev&quot;&gt;contact@gitroot.dev&lt;/a&gt; ou sur le fediverse &lt;cite class=&quot;informative&quot;&gt;&lt;a href=&quot;#forge@gitrrot.dev&quot;&gt;&lt;sup&gt;[forge@gitrrot.dev]&lt;/sup&gt;&lt;/a&gt;&lt;/cite&gt;(&lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;https://gts.gitroot.dev/@forge&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;qu-est-ce-qu-on-peut-faire&quot;&gt;Qu&amp;rsquo;est-ce qu&amp;rsquo;on peut faire ?&lt;/h2&gt;

&lt;h3 id=&quot;corriger-les-fautes&quot;&gt;Corriger les fautes&lt;/h3&gt;

&lt;p&gt;L&amp;rsquo;anglais n&amp;rsquo;est pas ma langue d&amp;rsquo;origine et je pense qu&amp;rsquo;il y a beaucoup de fautes, donc une tache pourrais être de lire tous les md dans &lt;code&gt;/doc/&lt;/code&gt;, &lt;code&gt;/blog/&lt;/code&gt; et pourquoi pas &lt;code&gt;/issues/&lt;/code&gt;. Vous corrigez, faites une branche et poussez. Je m&amp;rsquo;occupe du reste !&lt;/p&gt;

&lt;h3 id=&quot;moi-je-veux-faire-du-web&quot;&gt;Moi je veux faire du web&lt;/h3&gt;

&lt;p&gt;Ce qui serait top, ce serait de créer (ou de trouver) un thème. Aujourd&amp;rsquo;hui GitRoot utilise &lt;a target=&quot;_blank&quot; href=&quot;https://simplecss.org/&quot;&gt;simplecss&lt;/a&gt;, et il n&amp;rsquo;a besoin que de &lt;code&gt;css classless&lt;/code&gt;, il en existe beaucoup mais vous pouvez aussi créer celui de GitRoot.&lt;/p&gt;

&lt;p&gt;Le plus simple pour essayer est de lancer la commande &lt;code&gt;make testsuite&lt;/code&gt; quand elle a fini rendez vous dans &lt;code&gt;cd /tmp/repo1&lt;/code&gt; éditez le fichier &lt;code&gt;style.css&lt;/code&gt; puis &lt;code&gt;git add .&lt;/code&gt; &lt;code&gt;git commit -m &amp;quot;try style&amp;quot;&lt;/code&gt; et &lt;code&gt;git push&lt;/code&gt;, ouvrez &lt;a target=&quot;_blank&quot; href=&quot;http://127.0.0.1:4546/repo1&quot;&gt;http://127.0.0.1:4546/repo1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Quand vous êtes content faites une branche avec votre css dans &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/resources/styles/&quot;&gt;apex&lt;/a&gt;, puis &lt;code&gt;git add .&lt;/code&gt; et &lt;code&gt;git push&lt;/code&gt; je m&amp;rsquo;occupe du reste.&lt;/p&gt;

&lt;h3 id=&quot;moi-je-veux-coder&quot;&gt;Moi je veux coder&lt;/h3&gt;

&lt;p&gt;Bien, pas de soucis, voici une liste de petites tâches (enfin elles me paraissent petites mais si c&amp;rsquo;est pas le cas pour vous dites le moi):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;en golang:

&lt;ul&gt;
&lt;li&gt;dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/grafter/&quot;&gt;grafter&lt;/a&gt; pouvoir afficher &lt;a href=&quot;issues/3d33-grafter-dco.html&quot;&gt;autre chose&lt;/a&gt; dans le 1er message d&amp;rsquo;un graft. La bannière est en dure dans le plugin, il faudrait l&amp;rsquo;exporter dans la conf du plugin et l&amp;rsquo;utiliser.&lt;/li&gt;
&lt;li&gt;dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/silo/&quot;&gt;silo&lt;/a&gt; permettre de &lt;a href=&quot;issues/76ec-silo-more-selects.html&quot;&gt;grouper par fichier ou date&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;plus difficile mais j&amp;rsquo;aimerais bien avoir plus de &lt;a href=&quot;issues/e6d8-apex-alert-support.html&quot;&gt;renderer&lt;/a&gt; dans &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/&quot;&gt;apex&lt;/a&gt;, par exemple les infos, warning et autre&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;en typescript:

&lt;ul&gt;
&lt;li&gt;dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; il y a un bug sur le rss &lt;a href=&quot;issues/3986-pollen-rss-grow-infinite.html&quot;&gt;il est jamais nettoyé&lt;/a&gt;. Rajouté une option (ou pas, 100 en dur me semble être déjà pas mal) pour limiter le nombre d&amp;rsquo;&lt;code&gt;&amp;lt;item&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;toujours dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; pourquoi pas rajouter un &lt;a target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/JSON_Feed&quot;&gt;json feed&lt;/a&gt; en plus du rss. On peut serializer du json en assemblescript.&lt;/li&gt;
&lt;li&gt;plus difficile mais il serait bien de pouvoir dire au plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; de générer un rss avec le contenu des fichiers. Ça permettrait à GitRoot d&amp;rsquo;avoir un rss affichant les articles du blog par exemple. Aujourd&amp;rsquo;hui il ne met dans le rss que les commits.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;en rust:

&lt;ul&gt;
&lt;li&gt;faire ce que Ygor à dit dans &lt;a target=&quot;_blank&quot; href=&quot;https://floss.social/@ygor/115474535138702961&quot;&gt;https://floss.social/@ygor/115474535138702961&lt;/a&gt; je ne suis pas un developpeur rust et transformer des &lt;code&gt;String&lt;/code&gt; en &lt;code&gt;&amp;amp;str&lt;/code&gt; me parle pas beaucoup mais si vous l&amp;rsquo;êtes je suis sûr que c&amp;rsquo;est une petite tâche. Le code se trouve dans le &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; et le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/hop/&quot;&gt;hop&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;ajouter un &lt;a href=&quot;issues/9f00-rust-sdk-fake-server-test.html&quot;&gt;fakeServer&lt;/a&gt; au &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; pour faciliter les tests d&amp;rsquo;un plugin, voici celui de go &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&quot;&gt;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;moi-je-suis-sysadmin&quot;&gt;Moi je suis sysadmin&lt;/h3&gt;

&lt;p&gt;Parfait il y a de quoi faire aussi:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;aujourd&amp;rsquo;hui il y a trop d&amp;rsquo;étapes pour releaser GitRoot, &lt;a href=&quot;issues/dba0-simplify-release.html&quot;&gt;automatiser&lt;/a&gt; au maximum m&amp;rsquo;aiderais beaucoup.&lt;/li&gt;
&lt;li&gt;si vous avez un petit de place sur un de vos serveurs, &lt;a href=&quot;doc/tutorials/deploy_instance.html&quot;&gt;tester de déployer une instance&lt;/a&gt; juste pour le fun. Peu être vous pourrez améliorer la doc ou trouver quelque chose qui ne marche plus. Ou ajouter un paragraphe pour utiliser gitroot avec nginx ou autre.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;moi-je-veux-tester&quot;&gt;Moi je veux tester&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;tous les tests sont en bash dans &lt;a href=&quot;app/testsuite/runtest.sh&quot;&gt;testsuite/runtest.sh&lt;/a&gt; se trouve le plus gros test, à côté il y en d&amp;rsquo;autres

&lt;ul&gt;
&lt;li&gt;exécutez les pour voir s&amp;rsquo;ils marchent chez vous me parait une bonne première tâche&lt;/li&gt;
&lt;li&gt;en créer de nouveaux&lt;/li&gt;
&lt;li&gt;améliorer les existants en sortant par exemple des fonctions helper comme &lt;code&gt;quiet_git()&lt;/code&gt;, &lt;code&gt;report()&lt;/code&gt; ou &lt;code&gt;mySleep()&lt;/code&gt; dans d&amp;rsquo;autres fichiers (et les importer) serait d&amp;rsquo;une grande aide pour le projet. Vous pouvez aussi créer des nouveaux pour &lt;code&gt;killGitroot()&lt;/code&gt; qui est fait en début de chaque fichier. Ou &lt;code&gt;createProject()&lt;/code&gt; qui consiste à chaque fois à aller dans le root repository et ajouter son nom dans le fichier &lt;code&gt;.gitroot/repositories.yml&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;moi-je-veux-juste-réfléchir-discutter&quot;&gt;Moi je veux juste réfléchir/discutter&lt;/h3&gt;

&lt;p&gt;Tu es le bienvenue sur le &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;channel gitroot&lt;/a&gt; dans matrix. Ou par mail &lt;code&gt;contact [AT] gitroot.dev&lt;/code&gt; ou sur le fediverse &lt;code&gt;@forge@gitroot.dev&lt;/code&gt; &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;sur ce serveur&lt;/a&gt;.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/how-to-help_fr_11-2025.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/how-to-help_en_11-2025.md</link><title>How to help building GitRoot</title><pubDate>2025/11/18</pubDate><description>&lt;h1 id=&quot;how-to-help-building-gitroot-11-2025&quot;&gt;How to help building GitRoot 11-2025&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;This article is the translation of &lt;a href=&quot;blog/how-to-help_fr_11-2025.html&quot;&gt;french version&lt;/a&gt; thanks to &lt;a target=&quot;_blank&quot; href=&quot;https://www.deepl.com&quot;&gt;DeepL&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I just read &lt;a target=&quot;_blank&quot; href=&quot;https://antonin.delpeuch.eu/posts/too-much-time-on-your-hands/&quot;&gt;this article&lt;/a&gt; and I can&amp;rsquo;t stop thinking about it. I&amp;rsquo;m putting it into perspective for GitRoot contributors. I&amp;rsquo;m the only contributor (apart from my friend Damien, who corrected numerous typos, thank you to him) on this project. And as this &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge/statuses/01K98BCFEB9V430JD9TFQ32YZN&quot;&gt;toot&lt;/a&gt; shows, I have a job to pay the bills, a wife and two children who take up practically all the rest of my time. On top of that, I have GitRoot. For now, I&amp;rsquo;m having a blast and hanging in there, but how long can I keep it up?&lt;/p&gt;

&lt;p&gt;So why not have external contributors on GitRoot?&lt;/p&gt;

&lt;p&gt;I often tell myself that the project is young, too young, to be accessible to an external developer. Sometimes I think that a better issues system would be necessary to have a “welcome new contribution” label. Sometimes I think that once I&amp;rsquo;ve deployed the SDK plugins and created a plugin marketplace, then you can come and help me make GitRoot a true open-source project.&lt;/p&gt;

&lt;p&gt;But, because there is a but, this list will never end. Let&amp;rsquo;s be honest, there will always be something else to do before you can help me make GitRoot the best it can be. So I decided to do the simplest thing possible right now to explain how you can help me: a blog post.&lt;/p&gt;

&lt;p&gt;Here, I&amp;rsquo;ll explain everything that can be done in the current state of the project. Not what might be possible someday, but what you can do right now. I&amp;rsquo;ll repeat this kind of article as often as necessary.&lt;/p&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;There isn&amp;rsquo;t much you need to do to get ready to help out on GitRoot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you need to have &lt;a target=&quot;_blank&quot; href=&quot;https://git-scm.com/&quot;&gt;git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;git clone ssh://gitroot.dev&lt;/li&gt;
&lt;li&gt;cd gitroot.dev&lt;/li&gt;
&lt;li&gt;./gitroot/init.sh&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to code, you will also need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/bash/manual/bash.html&quot;&gt;bash&lt;/a&gt; and &lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/make/&quot;&gt;make&lt;/a&gt; (if you are a developer, chances are you already have them)&lt;/li&gt;
&lt;li&gt;Install &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt;, which is the easiest way to get all the right versions of the compilers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And now you&amp;rsquo;re ready to start helping. But what should you do? Well, like any good developer, I&amp;rsquo;ll answer, &amp;ldquo;It depends.&amp;rdquo;. It depends on what you want to do! I&amp;rsquo;ll list a set of possible tasks below, and you can choose.&lt;/p&gt;

&lt;h2 id=&quot;before-we-begin&quot;&gt;Before we begin&lt;/h2&gt;

&lt;p&gt;Before telling you what you can do, I thought it would be a good idea to explain what I am doing. So, for this version 0.3.0, my top priority is to make sure that GitRoot builds GitRoot itself. Currently, for each release, I build the binaries on my machine and then scp them. I want this to no longer be the case with version 0.3.0.&lt;/p&gt;

&lt;p&gt;So, if I can restrain myself, I shouldn&amp;rsquo;t have to do the following tickets. You have all the time you need to do them. The easiest thing, I think, is to meet up on &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;matrix channel gitroot&lt;/a&gt; to discuss them.&lt;/p&gt;

&lt;p&gt;We can also talk about it by email &lt;a href=&quot;blog/contact@gitroot.dev&quot;&gt;contact@gitroot.dev&lt;/a&gt; or on the fediverse &lt;cite class=&quot;informative&quot;&gt;&lt;a href=&quot;#forge@gitrrot.dev&quot;&gt;&lt;sup&gt;[forge@gitrrot.dev]&lt;/sup&gt;&lt;/a&gt;&lt;/cite&gt;(&lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;https://gts.gitroot.dev/@forge&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;what-can-we-do&quot;&gt;What can we do?&lt;/h2&gt;

&lt;h3 id=&quot;correct-mistakes&quot;&gt;Correct mistakes&lt;/h3&gt;

&lt;p&gt;English is not my native language, and I think there are a lot of typos, so one task could be to read all the MD files in &lt;code&gt;/doc/&lt;/code&gt;, &lt;code&gt;/blog/&lt;/code&gt;, and maybe even &lt;code&gt;/issues/&lt;/code&gt;. Correct them, create a branch, and push. I&amp;rsquo;ll take care of the rest!&lt;/p&gt;

&lt;h3 id=&quot;i-want-to-work-on-the-web&quot;&gt;I want to work on the web&lt;/h3&gt;

&lt;p&gt;It would be great to create (or find) a theme. GitRoot currently uses &lt;a target=&quot;_blank&quot; href=&quot;https://simplecss.org/&quot;&gt;simplecss&lt;/a&gt;, and it only needs &lt;code&gt;classless CSS&lt;/code&gt;. There are many available, but you can also create one for GitRoot.&lt;/p&gt;

&lt;p&gt;The easiest way to try it out is to run the &lt;code&gt;make testsuite&lt;/code&gt; command. When it&amp;rsquo;s finished, go to &lt;code&gt;cd /tmp/repo1&lt;/code&gt;, edit the &lt;code&gt;style.css&lt;/code&gt; file, then &lt;code&gt;git add .&lt;/code&gt; &lt;code&gt;git commit -m &amp;quot;try style&amp;quot;&lt;/code&gt; and &lt;code&gt;git push&lt;/code&gt;. Open &lt;a target=&quot;_blank&quot; href=&quot;http://127.0.0.1:4546/repo1&quot;&gt;http://127.0.0.1:4546/repo1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When you are satisfied, create a branch with your CSS in &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/resources/styles/&quot;&gt;apex&lt;/a&gt;, then &lt;code&gt;git add .&lt;/code&gt; and &lt;code&gt;git push&lt;/code&gt;. I&amp;rsquo;ll take care of the rest.&lt;/p&gt;

&lt;h3 id=&quot;i-want-to-code&quot;&gt;I want to code&lt;/h3&gt;

&lt;p&gt;Okay, no worries, here&amp;rsquo;s a list of small tasks (well, they seem small to me, but if that&amp;rsquo;s not the case for you, let me know):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;in golang:

&lt;ul&gt;
&lt;li&gt;in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/grafter/&quot;&gt;grafter&lt;/a&gt; plugin, be able to display &lt;a href=&quot;issues/3d33-grafter-dco.html&quot;&gt;something else&lt;/a&gt; in the first message of a graft. The banner is hard-coded in the plugin; it would need to be exported to the plugin configuration and used.&lt;/li&gt;
&lt;li&gt;In the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/silo/&quot;&gt;silo&lt;/a&gt; plugin, allow &lt;a href=&quot;issues/76ec-silo-more-selects.html&quot;&gt;grouping by file or date&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;More difficult, but I would like to have more &lt;a href=&quot;issues/e6d8-apex-alert-support.html&quot;&gt;renderers&lt;/a&gt; in &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/&quot;&gt;apex&lt;/a&gt;, for example info, warnings, and others&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;in typescript:

&lt;ul&gt;
&lt;li&gt;in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; plugin there is a bug in the RSS feed &lt;a href=&quot;issues/3986-pollen-rss-grow-infinite.html&quot;&gt;it is never cleaned up&lt;/a&gt;. Added an option (or not, 100 seems fine to me) to limit the number of &lt;code&gt;&amp;lt;item&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Still in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; plugin, why not add a &lt;a target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/JSON_Feed&quot;&gt;json feed&lt;/a&gt; in addition to the RSS feed? We can serialize json in assemblescript.&lt;/li&gt;
&lt;li&gt;More difficult, but it would be nice to be able to tell the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; plugin to generate an RSS feed with the contents of the files. This would allow GitRoot to have an RSS feed displaying blog articles, for example. Currently, it only puts commits in the RSS feed.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;in Rust:

&lt;ul&gt;
&lt;li&gt;Do what Ygor said in &lt;a target=&quot;_blank&quot; href=&quot;https://floss.social/@ygor/115474535138702961&quot;&gt;https://floss.social/@ygor/115474535138702961&lt;/a&gt; I&amp;rsquo;m not a Rust developer and converting &lt;code&gt;String&lt;/code&gt; to &lt;code&gt;&amp;amp;str&lt;/code&gt; doesn&amp;rsquo;t mean much to me, but if you are, I&amp;rsquo;m sure it&amp;rsquo;s a small task. The code can be found in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; and the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/hop/&quot;&gt;hop&lt;/a&gt; plugin.&lt;/li&gt;
&lt;li&gt;Add a &lt;a href=&quot; issues/9f00-rust-sdk-fake-server-test.html&quot;&gt;fakeServer&lt;/a&gt; to the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; to facilitate plugin testing. Here is the one for Go &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&quot;&gt;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;i-m-a-sysadmin&quot;&gt;I&amp;rsquo;m a sysadmin.&lt;/h3&gt;

&lt;p&gt;Perfect, there&amp;rsquo;s plenty to do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Today, there are too many steps to release GitRoot. &lt;a href=&quot;issues/dba0-simplify-release.html&quot;&gt;Automating&lt;/a&gt; as much as possible would help me a lot.&lt;/li&gt;
&lt;li&gt;If you have a little space on one of your servers, &lt;a href=&quot;doc/tutorials/deploy_instance.html&quot;&gt;try deploying an instance&lt;/a&gt; just for fun. Maybe you can improve the documentation or find something that no longer works. Or add a paragraph on using gitroot with nginx or something else.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;i-want-to-test&quot;&gt;I want to test&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;All tests are in bash in &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/testsuite/&quot;&gt;/app/testsuite/&lt;/a&gt;. The largest test is located in &lt;code&gt;runtest.sh&lt;/code&gt;, along with others.

&lt;ul&gt;
&lt;li&gt;Running them to see if they work on your system seems like a good first task to me.&lt;/li&gt;
&lt;li&gt;Creating new ones&lt;/li&gt;
&lt;li&gt;Improving existing ones by, for example, moving helper functions such as &lt;code&gt;quiet_git()&lt;/code&gt;, &lt;code&gt;report()&lt;/code&gt;, or &lt;code&gt;mySleep()&lt;/code&gt; to other files (and importing them) would be a great help to the project. You can also create new ones for &lt;code&gt;killGitroot()&lt;/code&gt;, which is done at the beginning of each file. Or &lt;code&gt;createProject()&lt;/code&gt;, which consists of going to the root repository each time and adding its name to the &lt;code&gt;.gitroot/repositories.yml&lt;/code&gt; file&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;i-just-want-to-think-discuss&quot;&gt;I just want to think/discuss&lt;/h3&gt;

&lt;p&gt;You are welcome to join the &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;gitroot channel&lt;/a&gt; on Matrix. Or contact us by email at &lt;code&gt;contact [AT] gitroot.dev&lt;/code&gt; or on the Fediverse at &lt;code&gt;@forge@gitroot.dev&lt;/code&gt; &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;on this server&lt;/a&gt;.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/how-to-help_en_11-2025.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/issues.md</link><title>Issues, Tickets, Discussions... Call them what you want!</title><pubDate>2026/06/08</pubDate><description>&lt;h1 id=&quot;issues-tickets-discussions-call-them-what-you-want&quot;&gt;Issues, Tickets, Discussions&amp;hellip; Call them what you want!&lt;/h1&gt;

&lt;p&gt;Many of you, when checking out &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev&quot;&gt;https://gitroot.dev&lt;/a&gt;, have told me that the &lt;code&gt;🐞 Issues&lt;/code&gt; button shouldn&amp;rsquo;t be labeled &amp;ldquo;Issues&amp;rdquo;, but rather &amp;ldquo;Tickets&amp;rdquo; or &amp;ldquo;Discussions&amp;rdquo;.&lt;/p&gt;

&lt;p&gt;First of all, thanks for the feedback! I really appreciate it, that&amp;rsquo;s how I can build a better forge. Of course, I will change the label, but developing a forge means having a lot of different areas to work on: issues/boards, git, code, CI/CD, and with GitRoot, even more, like plugins and WASM&amp;hellip;&lt;/p&gt;

&lt;p&gt;So, why don&amp;rsquo;t I just change this label right away? In reality, this label isn&amp;rsquo;t hardcoded inside GitRoot. It&amp;rsquo;s only there because I configured plugins to display it. I want to take this opportunity to explain how GitRoot works, because the platform is developed around this exact concept: how to customize a forge at the project level.&lt;/p&gt;

&lt;h2 id=&quot;the-original-idea&quot;&gt;The Original Idea&lt;/h2&gt;

&lt;p&gt;Imagine you are developing a forge, and to avoid vendor lock-in, you decide not to put anything outside of Git. You choose not to use any database at all. In this context, how do you design an &amp;ldquo;issue&amp;rdquo; system?&lt;/p&gt;

&lt;p&gt;In traditional forges, an &amp;ldquo;issue&amp;rdquo; consists of a title and a description created by a user. We can represent it like this:&lt;/p&gt;
&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;379.68002&quot; height=&quot;332.2&quot; viewBox=&quot;0 0 379.68002 332.2&quot;&gt;&lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;379.68002&quot; height=&quot;332.2&quot; fill=&quot;transparent&quot;/&gt;&lt;defs&gt;&lt;marker id=&quot;arrow-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 10 5 L 0 10 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-start-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;4.5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 5 L 10 10 L 10 0 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-open-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;1&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1 7 L 18 13 V 1 Z&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-open-start-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;18&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1 7 L 18 13 V 1 Z&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-dep-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;13&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 18 7 L 9 13 L 14 7 L 9 1 Z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-dep-start-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;6&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 5 7 L 9 13 L 1 7 L 9 1 Z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;path id=&quot;edge-0&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-0&quot; d=&quot;M 135.960,121.250 L 96.540,175.250&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot; x=&quot;85.37&quot; y=&quot;134.25&quot; width=&quot;61.76&quot; height=&quot;28.00&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.85&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.35&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;116.25&quot; y=&quot;151.75&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;116.25&quot; dy=&quot;0.00&quot;&gt;Author&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-1&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-1&quot; d=&quot;M 231.960,121.250 L 255.380,175.250&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-1&quot; data-label-kind=&quot;center&quot; x=&quot;217.27&quot; y=&quot;134.25&quot; width=&quot;52.80&quot; height=&quot;28.00&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.85&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.35&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-1&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;243.67&quot; y=&quot;151.75&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;243.67&quot; dy=&quot;0.00&quot;&gt;State&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;rect x=&quot;87.90&quot; y=&quot;8.00&quot; width=&quot;192.12&quot; height=&quot;113.25&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;line x1=&quot;93.90&quot; y1=&quot;46.95&quot; x2=&quot;274.02&quot; y2=&quot;46.95&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1.0&quot;/&gt;&lt;text x=&quot;183.96&quot; y=&quot;34.00&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;183.96&quot; dy=&quot;0.00&quot; font-weight=&quot;600&quot;&gt;Issue&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;117.90&quot; y=&quot;69.70&quot; text-anchor=&quot;start&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;117.90&quot; dy=&quot;0.00&quot;&gt;int id&lt;/tspan&gt;&lt;tspan x=&quot;117.90&quot; dy=&quot;17.85&quot;&gt;string title&lt;/tspan&gt;&lt;tspan x=&quot;117.90&quot; dy=&quot;17.85&quot;&gt;string description&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;203.08&quot; y=&quot;175.25&quot; width=&quot;168.60&quot; height=&quot;148.95&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;line x1=&quot;209.08&quot; y1=&quot;214.20&quot; x2=&quot;365.68&quot; y2=&quot;214.20&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1.0&quot;/&gt;&lt;text x=&quot;287.38&quot; y=&quot;201.25&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;287.38&quot; dy=&quot;0.00&quot; font-weight=&quot;600&quot;&gt;Status&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;233.08&quot; y=&quot;236.95&quot; text-anchor=&quot;start&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;0.00&quot;&gt;&amp;lt;&amp;lt;enumeration&amp;gt;&amp;gt;&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;Open&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;TODO&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;DOING&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;CLOSE&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;8.00&quot; y=&quot;175.25&quot; width=&quot;145.08&quot; height=&quot;95.40&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;line x1=&quot;14.00&quot; y1=&quot;214.20&quot; x2=&quot;147.08&quot; y2=&quot;214.20&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1.0&quot;/&gt;&lt;text x=&quot;80.54&quot; y=&quot;201.25&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;80.54&quot; dy=&quot;0.00&quot; font-weight=&quot;600&quot;&gt;User&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;38.00&quot; y=&quot;236.95&quot; text-anchor=&quot;start&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;38.00&quot; dy=&quot;0.00&quot;&gt;int id&lt;/tspan&gt;&lt;tspan x=&quot;38.00&quot; dy=&quot;17.85&quot;&gt;string email&lt;/tspan&gt;&lt;/text&gt;&lt;/svg&gt;
&lt;p&gt;Easy, right? Well, how do you represent that in GitRoot using only files? This question was running through my mind for months before I finally found a solution.&lt;/p&gt;

&lt;h2 id=&quot;the-basic-solution&quot;&gt;The Basic Solution&lt;/h2&gt;

&lt;p&gt;Since I only have files at my disposal, what actually constitutes an &amp;ldquo;issue&amp;rdquo;? I need to add some metadata to a file so the system understands that this file is an issue and not, say, a &lt;code&gt;main.go&lt;/code&gt;. I’ll probably want to group all these &amp;ldquo;issues&amp;rdquo; into a specific directory, because while GitRoot technically lets you do whatever you want (even dropping an issue right in the middle of your source code), I personally prefer to keep my files organized!&lt;/p&gt;

&lt;p&gt;A simple text format that handles metadata perfectly is Markdown. I use Markdown here, but any other markup language could work. Let&amp;rsquo;s create a file named &lt;code&gt;issues/1.md&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;id: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;user: email@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;status: OPEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# My title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt;9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;My description where I can detail what my problem is.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yes, it really is that simple. In GitRoot, to create an issue, you just create a file, add some metadata, commit, and push. Boom, you have an issue.&lt;/p&gt;

&lt;p&gt;Now, you might be thinking: &amp;ldquo;Well, I lose a lot of control with that, the status could be anything!&amp;rdquo; or &amp;ldquo;How are other users supposed to know what to put where?&amp;rdquo;. You&amp;rsquo;re right, and that is exactly where plugins enter the game.&lt;/p&gt;

&lt;h2 id=&quot;a-better-solution&quot;&gt;A Better Solution&lt;/h2&gt;

&lt;p&gt;If I simplify this as much as possible, what does an external contributor actually need to provide to create an issue? Just a title and a description. So, let&amp;rsquo;s create a file named &lt;code&gt;issues/my_issue.md&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# My title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;My description where I can detail what my problem is.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we run &lt;code&gt;git commit &amp;amp;&amp;amp; git push&lt;/code&gt;. Since I have configured the &lt;a href=&quot;plugins/name/ladybug.html&quot;&gt;ladybug plugin&lt;/a&gt; like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: ladybug
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: issues/**/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;func&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: issues/**/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;callfunc&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;default&lt;/span&gt;: autogenerated
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;mandatory&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: id
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;type&lt;/span&gt;: crc16
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;default&lt;/span&gt;: OPEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;mandatory&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: status
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;type&lt;/span&gt;: string
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;default&lt;/span&gt;: email@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;mandatory&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: user
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;type&lt;/span&gt;: string
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can read this configuration as: &amp;ldquo;For every Markdown file in the &lt;code&gt;issues&lt;/code&gt; directory across all branches, if the file doesn&amp;rsquo;t have these metadata fields, automatically add these default values.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, if you are a Git purist, a server creating commits right on top of yours might sound a bit radical, or even terrifying. But look closely at the &lt;code&gt;write&lt;/code&gt; block in the YAML configuration above:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: issues/**/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;GitRoot doesn&amp;rsquo;t just let plugins run wild, the project configuration must explicitly grant the plugin permission to write or modify files on specific paths. If you don&amp;rsquo;t declare these rights, GitRoot will block the plugin from making any changes.&lt;/p&gt;

&lt;p&gt;When granted, GitRoot intercepts your push, lets the plugin inject the missing metadata, and safely generates a follow-up commit. And don&amp;rsquo;t worry about your history: if you decide to &lt;code&gt;git rebase -i&lt;/code&gt; later to squash or fixup your commits, plugins won&amp;rsquo;t generate duplicate commits if no metadata has changed.&lt;/p&gt;

&lt;p&gt;What about security or external users trying to forge metadata? Since contributors cannot push directly to the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt; (like &lt;code&gt;main&lt;/code&gt;), they have to go through a branch and need to be merged. This means every single change to an issue&amp;rsquo;s status or metadata is subjected to a code review.&lt;/p&gt;

&lt;p&gt;If an external user tries to close someone else&amp;rsquo;s issue or manipulate IDs, it will stand out like a sore thumb in the git diff. Maintainers retain absolute control and can simply reject the rogue contribution. Security doesn&amp;rsquo;t rely on complex database permission tables, but on the battle-tested git review workflow you already know and trust.&lt;/p&gt;

&lt;p&gt;So, once you push, the server-side plugin does its job, and the next time you run &lt;code&gt;git pull&lt;/code&gt;, your local file will automatically look like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;id: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;user: email@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;status: OPEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# My title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt;9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;My description where I can detail what my problem is.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bingo! You get the exact same file, with the exact same metadata you wanted. Of course, a lot more needs to be added, like defining a &lt;code&gt;type: user&lt;/code&gt; to assign an issue, or perhaps a &lt;code&gt;type: status&lt;/code&gt; to define an enum of allowed values. But I think you see where I&amp;rsquo;m going with this feature!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: If you&amp;rsquo;re wondering about the barrier to entry for non-technical contributors, my long-term vision is to allow plugins to intercept standard HTTP requests. Tomorrow, a classic web form will be able to POST to GitRoot and automatically commit a &lt;code&gt;.md&lt;/code&gt; file under the hood. But for now, we are proudly dev-centric!)&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;so-what-about-the-label&quot;&gt;So, What About the Label?&lt;/h2&gt;

&lt;p&gt;Throughout all of this, you’ll notice that the term &amp;ldquo;issue&amp;rdquo; only appears within the plugins themselves. Nowhere have we talked about that &lt;code&gt;🐞 Issues&lt;/code&gt; button yet. We actually need to introduce another plugin before we can tackle that topic.&lt;/p&gt;

&lt;p&gt;First, we need a file that lists all of these &amp;ldquo;issues&amp;rdquo;. That’s exactly why the &lt;a href=&quot;plugins/name/silo.html&quot;&gt;silo plugin&lt;/a&gt; exists. It inspects all files and groups them whenever it finds a matching pattern.&lt;/p&gt;

&lt;p&gt;To list all open &amp;ldquo;issues&amp;rdquo;, we configure it like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: silo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;func&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: boards/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - append
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;callfunc&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;boards&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;description&lt;/span&gt;: All open issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;for&lt;/span&gt;: issues/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;format&lt;/span&gt;: table
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;paginator&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;50&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;selects&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;status: (.*)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;user: (.*)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;sort&lt;/span&gt;: select[0]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;sortOrder&lt;/span&gt;: desc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;tableHeader&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;| | status | user |&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;title&lt;/span&gt;: All issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;to&lt;/span&gt;: boards/issues.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;where&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;status: OPEN&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can read this configuration as: &amp;ldquo;For every file in the main branch, create a board called &amp;lsquo;All issues&amp;rsquo; containing links to all files matching &lt;code&gt;status: OPEN&lt;/code&gt;. Display them as a table with &lt;code&gt;status&lt;/code&gt; and &lt;code&gt;user&lt;/code&gt; columns.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After running &lt;code&gt;git commit &amp;amp;&amp;amp; git push &amp;amp;&amp;amp; git pull&lt;/code&gt;, you will get a new file at &lt;code&gt;boards/issues.md&lt;/code&gt; containing something like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# All issues
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;All open issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;|                                   | status | user            |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;| --------------------------------- | ------ | --------------- |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;| [&lt;span style=&quot;color:#ff79c6&quot;&gt;My title&lt;/span&gt;](&lt;span style=&quot;color:#50fa7b&quot;&gt;../issues/my_issue.md&lt;/span&gt;) | OPEN   | email@email.com |
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Silo has aggregated all your open issues into a single dashboard. Of course, you could easily create another board for closed issues in a separate file like &lt;code&gt;boards/closed.md&lt;/code&gt;, it&amp;rsquo;s entirely up to you.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: If you are sweating at the thought of a plugin scanning your entire repository on every single push, don&amp;rsquo;t panic. &lt;code&gt;silo&lt;/code&gt;, thanks to the plugin-sdk API, doesn&amp;rsquo;t do a full disk scan. It only processes the incremental diffs generated by the incoming commit. This is precisely why we restrict its scope to the &lt;code&gt;main&lt;/code&gt; branch in this example.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now that we have a page representing all our issues, we can finally render it in the browser. Because, as you might have guessed, the label causing all this debate lives in the Web UI.&lt;/p&gt;

&lt;h2 id=&quot;the-web-is-where-it-happens&quot;&gt;The Web is Where It Happens&lt;/h2&gt;

&lt;p&gt;To render my &lt;code&gt;boards/issues.md&lt;/code&gt; file on the web, I use the &lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex&lt;/a&gt; plugin, in conjunction with the &lt;a href=&quot;plugins/name/apex_markdown.html&quot;&gt;apex_markdown&lt;/a&gt; plugin, of course.&lt;/p&gt;

&lt;p&gt;These plugins take files from git and render them directly in the Web UI. I configure them like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: apex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;func&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - append
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - append
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;callfunc&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;pluginname&lt;/span&gt;: apex_markdown
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;funcname&lt;/span&gt;: renderMd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;menu&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 🏠 Home
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 📖 Documentation
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L38&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L38&quot;&gt;38&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /doc/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L39&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L39&quot;&gt;39&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 🔖 Versions
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L40&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L40&quot;&gt;40&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /CHANGELOG.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L41&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L41&quot;&gt;41&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: �� Plugins
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L42&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L42&quot;&gt;42&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /plugins/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L43&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L43&quot;&gt;43&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: �� Issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L44&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L44&quot;&gt;44&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /boards/issues.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L45&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L45&quot;&gt;45&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 🚀 Code
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L46&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L46&quot;&gt;46&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /worktree/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L47&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L47&quot;&gt;47&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: ☕ Blog
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L48&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L48&quot;&gt;48&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /blog/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L49&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L49&quot;&gt;49&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: �� Contact
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L50&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L50&quot;&gt;50&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /contact.html
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oh, do you see it now? Yes, right there: &lt;code&gt;display: 🐞 Issues&lt;/code&gt;. This is the exact configuration that tells Apex to add a global menu item with the &lt;code&gt;🐞 Issues&lt;/code&gt; label, pointing to the &lt;code&gt;/boards/issues.html&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;If you are wondering how Apex magically knows where to find your &amp;ldquo;issues&amp;rdquo;, the secret is: it doesn&amp;rsquo;t.&lt;/p&gt;

&lt;p&gt;Apex isn&amp;rsquo;t a smart router, it&amp;rsquo;s just a file translator. It blindly takes the Markdown structure generated by GitRoot and renders it into HTML. Because I know Apex transforms &lt;code&gt;boards/issues.md&lt;/code&gt; into &lt;code&gt;boards/issues.html&lt;/code&gt;, I simply hardcode that target URL in my menu configuration (&lt;code&gt;link: /boards/issues.html&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;And what about the links inside the table? Apex has a clever built-in rewrite engine. When it parses your generated board file, it automatically rewrites every internal Markdown link, turning &lt;code&gt;[My title](../issues/my_issue.md)&lt;/code&gt; into a standard web-friendly &lt;code&gt;&amp;lt;a href=&amp;quot;../issues/my_issue.html&amp;quot;&amp;gt;&lt;/code&gt;. No database, no dynamic routing, just plain old static files mapped one-to-one.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;If you change the label to &lt;code&gt;🐞 Tickets&lt;/code&gt; or &lt;code&gt;💡 Discussions&lt;/code&gt;, the &amp;ldquo;issue to rename issues into something else&amp;rdquo; can finally be closed. Ideally, you might also want to rename some directories in the other plugins&amp;rsquo; configurations, but it&amp;rsquo;s really not a big deal.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;m pretty sure you now understand why I didn&amp;rsquo;t immediately jump on resolving this task: everything we just saw is pure configuration, completely separated from the GitRoot core code.&lt;/p&gt;

&lt;p&gt;And to be perfectly honest, I haven&amp;rsquo;t changed it yet because none of the traditional options really appeal to me. I don&amp;rsquo;t particularly like &amp;ldquo;Tickets&amp;rdquo; or &amp;ldquo;Discussions&amp;rdquo;, maybe it should be &amp;ldquo;Feature Requests&amp;rdquo; instead (and yes, you can now model that in GitRoot too!).&lt;/p&gt;

&lt;p&gt;In fact, I personally call them &lt;code&gt;TODO&lt;/code&gt;s, because in reality, they are all just waiting for me to get them done.&lt;/p&gt;

&lt;h3 id=&quot;wait-why-stop-at-issues&quot;&gt;Wait&amp;hellip; Why stop at issues?&lt;/h3&gt;

&lt;p&gt;This is where the true power of GitRoot will blow your mind.&lt;/p&gt;

&lt;p&gt;Because GitRoot doesn&amp;rsquo;t care about databases, and because everything is just a file inside Git, the &lt;code&gt;silo&lt;/code&gt; plugin can aggregate anything. I don&amp;rsquo;t need a separate &lt;code&gt;TODO&lt;/code&gt; page for my tickets, because I already have one that automatically tracks the &lt;code&gt;//TODO&lt;/code&gt; comments inside my actual source code.&lt;/p&gt;

&lt;p&gt;Yes, you read that right. You can visit it live at &lt;a href=&quot;boards/todos.html&quot;&gt;https://gitroot.dev/boards/todos.html&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Every time I write a &lt;code&gt;//TODO&lt;/code&gt; in a &lt;code&gt;.go&lt;/code&gt;, &lt;code&gt;.rs&lt;/code&gt; or &lt;code&gt;.ts&lt;/code&gt; file, GitRoot parses it, aggregates it, and generates a dynamic dashboard. Tickets, documentation, source code comments&amp;hellip; it&amp;rsquo;s all the exact same data structure.&lt;/p&gt;

&lt;p&gt;To be clear, it doesn’t magically transform your source code comments into full-blown database-like issues with IDs and statuses. It simply extracts them to give you a centralized, living view of your technical debt, right alongside your official tickets. It’s the exact same data aggregation pipeline, tailored for two different use cases.&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to discover more plugins and configurations, feel free to browse through the &lt;a href=&quot;plugins&quot;&gt;🧩 Plugins section&lt;/a&gt;. However, for now, the best way to explore remains checking out the actual &lt;a href=&quot;.gitroot/plugins.yml&quot;&gt;GitRoot plugins.yml configuration file&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
</description><guid>https://gitroot.dev//blog/issues.md</guid></item>
<item><author>romain@gitroot.dev (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.5.0.md</link><title>0.5.0</title><pubDate>2026/10/05</pubDate><description>&lt;nav&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#move-slowly-and-take-care-of-things&quot;&gt;Move slowly, and take care of things&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#breaking-purl&quot;&gt;Breaking PURL&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#a-lot-of-people-have-discovered-gitroot&quot;&gt;A lot of people have discovered GitRoot&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#not-conclusive&quot;&gt;Not conclusive&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#before-it-s-too-late&quot;&gt;Before it&amp;rsquo;s too late&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#my-new-favorite-language-until-the-next-one&quot;&gt;My new favorite language&amp;hellip;. until the next one&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#plugins-needs-more-power&quot;&gt;Plugins needs more power&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#sftp-to-save-the-web&quot;&gt;Sftp to save the web&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#how-many-bugs&quot;&gt;How many bugs?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#in-depth-details&quot;&gt;In-depth Details&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#purl-breaking-details&quot;&gt;PURL breaking details&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#gitroot-communication&quot;&gt;GitRoot communication&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#sdkgenerator-full-story&quot;&gt;SdkGenerator full story&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#max-anonymous-branches&quot;&gt;Max anonymous branches&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#zig-plugin-sdk&quot;&gt;Zig plugin-sdk&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#pluginsdk-improvements&quot;&gt;PluginSdk improvements&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#sftp-feature&quot;&gt;Sftp feature&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#some-interesting-bug-fixes&quot;&gt;Some interesting bug fixes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;#what-next&quot;&gt;What next?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;/nav&gt;

&lt;h1 id=&quot;move-slowly-and-take-care-of-things&quot;&gt;Move slowly, and take care of things&lt;/h1&gt;

&lt;p&gt;These days, everything moves too fast. They push thousands of lines of AI generated code every hour, deploying it without even understanding how it works. But with GitRoot, I want to do the exact opposite. I want to take my time, savor every architectural choice, and thoroughly enjoy writing every line of code by hand. It&amp;rsquo;s a project that is deeply close to my heart: a space for freedom far away from the rat race of metrics. So yes, we move at our own pace: &lt;code&gt;slowly, and take care of things&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote style=&quot;border-color: #006ac7&quot;&gt;

&lt;p&gt;Thanks to &lt;a target=&quot;_blank&quot; href=&quot;https://pouet.chapril.org/@lonugem&quot;&gt;lonugem&lt;/a&gt; for the title idea&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes&lt;/h2&gt;

&lt;h3 id=&quot;breaking-purl&quot;&gt;Breaking PURL&lt;/h3&gt;

&lt;p&gt;I&amp;rsquo;m sorry to say that the first feature I want to talk about is a big breaking one. I know, I know, on the homepage of GitRoot I say &amp;ldquo;no headaches to upgrade my forge&amp;rdquo;&amp;hellip;. but this time you will&amp;hellip; If you are not happy, you can contact the support and get a refund for the previous year and a 100% discount for the next one :D&lt;/p&gt;

&lt;p&gt;Joking aside, you need to replace your plugin URL with a PURL one. &lt;a target=&quot;_blank&quot; href=&quot;https://ecma-international.org/publications-and-standards/standards/ecma-427/&quot;&gt;ECMA PURL&lt;/a&gt; is a specification to produce &amp;ldquo;a standardized URL-based syntax that uniquely identifies software packages&amp;rdquo;.&lt;/p&gt;

&lt;p&gt;This corresponds to the following change:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#f55&quot;&gt;- url: https://gitroot.dev/releases/0.4.0/ladybug-0.0.4.wasm
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#f55&quot;&gt;- checksum: sha256:7409837469ce89d5c9fd8dd48236764650f48523ff4d81295967154977a01635
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#f55&quot;&gt;- name: ladybug
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#f55&quot;&gt;- version: 0.0.4
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#f55&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#50fa7b;font-weight:bold&quot;&gt;+ purl: pkg:gitroot/gitroot/ladybug@0.0.5?checksum=none&amp;amp;registry=https:%2F%2F%2Fgitroot.dev%2Freleases%2F0.5.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#50fa7b;font-weight:bold&quot;&gt;&lt;/span&gt;  active: true
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;GitRoot will download &lt;code&gt;$registry/$scope-$name-$version.wasm&lt;/code&gt;, so it will be &lt;code&gt;https://gitroot.dev/releases/0.5.0/gitroot-ladybug-0.0.5.wasm&lt;/code&gt; for the example. And then compare the downloaded WASM binary with the checksum provided in the PURL.&lt;/p&gt;
&lt;blockquote style=&quot;border-color: #be4700&quot;&gt;

&lt;p&gt;Look in the &lt;a href=&quot;plugins/index.html&quot;&gt;plugins registry&lt;/a&gt; to find the exact purl for each plugin.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To know why, how and more &lt;a href=&quot;blog/version-0.5.0.html#purl-breaking-details&quot;&gt;read purl details&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;a-lot-of-people-have-discovered-gitroot&quot;&gt;A lot of people have discovered GitRoot&lt;/h3&gt;

&lt;p&gt;First show, first lobste.rs/HN links, first video&amp;hellip; Yes, more and more people are talking about GitRoot. &lt;a href=&quot;blog/version-0.5.0.html#gitroot-communication&quot;&gt;Discover where&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;not-conclusive&quot;&gt;Not conclusive&lt;/h3&gt;

&lt;p&gt;Sometimes you spend more time on a subject than you thought. And sometimes, after all of this time spent, you don&amp;rsquo;t have what you wanted to have at the start&amp;hellip; That is the case for the &lt;code&gt;sdkgenerator&lt;/code&gt; experiment. It should &amp;ldquo;just&amp;rdquo; generate Rust, TS, Zig code from Golang&amp;hellip; &amp;ldquo;Just&amp;rdquo;, lol, I never finished it. Maybe someone wants help? &lt;a href=&quot;blog/version-0.5.0.html#sdkgenerator-full-story&quot;&gt;Read the full sdkgenerator story&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;before-it-s-too-late&quot;&gt;Before it&amp;rsquo;s too late&lt;/h3&gt;

&lt;p&gt;How to prevent spam with a forge without registration? This question was asked by NLnet 2 years ago (no, they don&amp;rsquo;t fund GitRoot, I was rejected). Two years later I have the answer, not perfect, but it&amp;rsquo;s a start. I added two new properties: one in &lt;code&gt;.gitroot/forgeConf.yml&lt;/code&gt; of your root repository, &lt;code&gt;NbMaxBranchesPerProjectBeforeRejectingAnonymous&lt;/code&gt;, which is the number of branches before rejecting anonymous pushes. And one in &lt;code&gt;.gitroot/repoConfiguration.yml&lt;/code&gt; of all your repositories, &lt;code&gt;NbMaxBranchesBeforeRejectingAnonymous&lt;/code&gt;, which serves the same purpose but for a single project. &lt;a href=&quot;blog/version-0.5.0.html#max-anonymous-branches&quot;&gt;Read why and see the full example&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;my-new-favorite-language-until-the-next-one&quot;&gt;My new favorite language&amp;hellip;. until the next one&lt;/h3&gt;

&lt;p&gt;After Golang, AssemblyScript, and Rust, the plugin-sdk is now accessible for the Zig language. I continue to add new languages and discover how they work. This one was especially refreshing in its implementation. &lt;a href=&quot;blog/version-0.5.0.html#zig-plugin-sdk&quot;&gt;Read more&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;plugins-needs-more-power&quot;&gt;Plugins needs more power&lt;/h3&gt;

&lt;p&gt;Talking about the plugin-sdk, they all receive a lot of new features. They can now &lt;code&gt;schedule&lt;/code&gt; jobs, make &lt;code&gt;http&lt;/code&gt; requests, and configure plugins during &lt;code&gt;CallFunc&lt;/code&gt;. I have also simplified the initialization part. &lt;a href=&quot;blog/version-0.5.0.html#pluginsdk-improvements&quot;&gt;Read details&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;sftp-to-save-the-web&quot;&gt;Sftp to save the web&lt;/h3&gt;

&lt;p&gt;How to add images to the web without cluttering Git? That was the problem I had to tackle before adding slideshows to GitRoot. SFTP came to the rescue, and with ~200 lines of code, it&amp;rsquo;s now possible. I love when a simple, established protocol allows you to fix a complicated feature. &lt;a href=&quot;blog/version-0.5.0.html#sftp-feature&quot;&gt;Read more&lt;/a&gt; to know how to enable it.&lt;/p&gt;

&lt;h3 id=&quot;how-many-bugs&quot;&gt;How many bugs?&lt;/h3&gt;

&lt;p&gt;A lot of bug fixes have been done. Let me explain &lt;a href=&quot;blog/version-0.5.0.html#some-interesting-bug-fixes&quot;&gt;some interesting ones&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;in-depth-details&quot;&gt;In-depth Details&lt;/h2&gt;

&lt;h3 id=&quot;purl-breaking-details&quot;&gt;PURL breaking details&lt;/h3&gt;

&lt;p&gt;At the beginning of this story, I only wanted to add a &lt;a href=&quot;issues/close/5dd3-plugin-scope.html&quot;&gt;scope&lt;/a&gt; for plugins. That way, if you install a plugin with &lt;code&gt;gitroot&lt;/code&gt; as the scope, you can think &amp;ldquo;it looks less dangerous&amp;rdquo; than &lt;code&gt;anyother&lt;/code&gt; scope. Just like I suspect less &lt;code&gt;@angular&lt;/code&gt; than some &lt;code&gt;odd&lt;/code&gt; Node.js package (well, the npm ecosystem is maybe a bad example for that&amp;hellip; but the only one right now).&lt;/p&gt;

&lt;p&gt;So, my simple idea was to transform &lt;code&gt;url: https://gitroot.dev/releases/0.4.0/ladybug-0.0.4.wasm&lt;/code&gt; into something like &lt;code&gt;url: https://gitroot.dev/releases/0.4.0/gitroot-ladybug-0.0.4.wasm&lt;/code&gt;, so that a &lt;code&gt;gitroot-*.wasm&lt;/code&gt; is affiliated with me. In the &lt;a href=&quot;plugins/index.html&quot;&gt;registry&lt;/a&gt;, I can show the scope.&lt;/p&gt;

&lt;p&gt;Another example is if someone forks &lt;code&gt;ladybug&lt;/code&gt; and wants to release it in the &lt;a href=&quot;plugins/index.html&quot;&gt;registry&lt;/a&gt;. Previously there was no option to do that, so adding a scope seemed to be the best choice.&lt;/p&gt;

&lt;p&gt;During my research on a way to model this scope, I found the &lt;a target=&quot;_blank&quot; href=&quot;https://ecma-international.org/publications-and-standards/standards/ecma-427/&quot;&gt;ECMA-427 Package-URL (PURL)&lt;/a&gt; specification. PURL stands for Package URL (I call them Plugin URL), a way to express many packages as a URL. What a perfect fit for the GitRoot plugin system!&lt;/p&gt;

&lt;p&gt;This is how a PURL is represented: &lt;code&gt;scheme:type/namespace/name@version?qualifiers#subpath&lt;/code&gt;. In GitRoot, I use only &lt;code&gt;pkg:gitroot/$scope/$name@$version?checksum=$checksum&amp;amp;registry=$urlToDownload&lt;/code&gt; (maybe a default registry like &lt;code&gt;https:%2F%2F%2Fgitroot.dev&lt;/code&gt; will be added in a next version, I&amp;rsquo;m not sure about that for now). There is no specification about what a registry should do to implement the GitRoot specification. For now, GitRoot will download &lt;code&gt;$urlToDownload/$scope-$name-$version.wasm&lt;/code&gt;, and that&amp;rsquo;s all.&lt;/p&gt;

&lt;p&gt;Thanks to PURL, I can delete a lot of other properties in the &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt; file, like version, checksum, and name. I also use it a lot in the code to refer to a plugin. Previously I only used the name, but now I use a full PURL. So, for example, if a plugin wants to call another plugin (thanks to the &lt;code&gt;CallFunc&lt;/code&gt; API), it can define it as &lt;code&gt;pkg:gitroot/$scope/$name&lt;/code&gt; (to mean all possible versions), or an exact version with &lt;code&gt;pkg:gitroot/$scope/$name@$version&lt;/code&gt;. Adding a checksum and even a registry is also possible.&lt;/p&gt;

&lt;p&gt;That was a big refactor to do, but I&amp;rsquo;m pretty happy with the result.&lt;/p&gt;

&lt;h3 id=&quot;gitroot-communication&quot;&gt;GitRoot communication&lt;/h3&gt;

&lt;p&gt;A lot has occured during this summer, on the communication side too.&lt;/p&gt;

&lt;p&gt;I want to thank &lt;a target=&quot;_blank&quot; href=&quot;https://adullact.org/&quot;&gt;Adullact&lt;/a&gt; and particularly Matthieu for letting me present the first show at the &lt;a target=&quot;_blank&quot; href=&quot;https://adullact.org/congres&quot;&gt;Adullact congress&lt;/a&gt;. The video is only in French without subtitles: &lt;a target=&quot;_blank&quot; href=&quot;https://video.adullact.org/w/kYxpgoVZfoPcLsDe19jvQw&quot;&gt;Souveraineté : Savez-vous vraiment qui contrôle vos logiciels ?&lt;/a&gt;. It was a great two-day congress, I talked to a lot of passionate people. And for once, they weren&amp;rsquo;t developers.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://gitroot.dev/branches/v0.5/blog/version-0.5.0-cover.png&quot; alt=&quot;Chart with number of unique visitor, a uge pick (from 500 to 13k) is visible around end of jul start of aout.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Then the first appearance on &lt;a target=&quot;_blank&quot; href=&quot;https://lobste.rs/s/z01edy&quot;&gt;lobste.rs&lt;/a&gt; and &lt;a target=&quot;_blank&quot; href=&quot;https://news.ycombinator.com&quot;&gt;Hacker News&lt;/a&gt; occurred. Thanks &lt;a target=&quot;_blank&quot; href=&quot;https://fosstodon.org/@hasnep&quot;&gt;hasnep&lt;/a&gt; for sharing, you can follow the initial exchange on my &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge/statuses/01KXTPKHP5KVWHETKSMC24B32E&quot;&gt;GoToSocial&lt;/a&gt;. As the image shows, there was a spike in the number of visitors, emails, and questions asked on the platforms and in private. That was a great moment to experience.&lt;/p&gt;

&lt;p&gt;After the mentions on these platforms, some other cool articles popped up (I won&amp;rsquo;t link them as they are principally AI-generated slop). But there was also a video showing how to run GitRoot in a &lt;a target=&quot;_blank&quot; href=&quot;https://nanos.org/&quot;&gt;nanoVM&lt;/a&gt;: &lt;a target=&quot;_blank&quot; href=&quot;https://www.youtube.com/watch?v=g5nW9g5T4wU&quot;&gt;https://www.youtube.com/watch?v=g5nW9g5T4wU&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another particular point of pride was the first mention by &lt;a target=&quot;_blank&quot; href=&quot;https://nesbitt.io/&quot;&gt;Andrew Nesbitt&lt;/a&gt; in the blog article &lt;a target=&quot;_blank&quot; href=&quot;https://nesbitt.io/2026/08/20/issues-in-the-repo.html&quot;&gt;Issues in the Repo&lt;/a&gt;. Thanks to Damien for talking about GitRoot to Andrew. Since you deleted the toot, I guess you don&amp;rsquo;t want to be cited here. If that&amp;rsquo;s not the case, contact me.&lt;/p&gt;

&lt;p&gt;And GitRoot has been added to &lt;a target=&quot;_blank&quot; href=&quot;https://alternativeto.net/software/gitroot/about/&quot;&gt;AlternativeTo&lt;/a&gt;. I don&amp;rsquo;t know if this will lead to anything, but hey, I&amp;rsquo;m right alongside GitHub and GitLab :P&lt;/p&gt;

&lt;h3 id=&quot;sdkgenerator-full-story&quot;&gt;SdkGenerator full story&lt;/h3&gt;

&lt;p&gt;This experiment comes from a bored developer who didn&amp;rsquo;t want to use LLMs, but also didn&amp;rsquo;t want to rewrite the same models/objects/structs (call that what you want) four times. When I want to add a new function to the plugins-sdk, I do it in one language, and then, when it works, I need to rewrite it in all the other languages (more on that in the &amp;ldquo;&lt;a href=&quot;blog/modifying-API-SDK-plugin.html&quot;&gt;Modifying the API in the SDK Plugin&lt;/a&gt;&amp;rdquo; blog article).&lt;/p&gt;

&lt;p&gt;In my day job, I love to build tools that generate code. I have written a tool that takes Kotlin as input and generates TypeScript output. Then I wrote a tool that generates complete Angular components from Kotlin enums (yeah, bored of writing a form select from an enum). And finally, it generates complex Angular services from Spring annotations (when you are very bored, a lot of things can be generated).&lt;/p&gt;

&lt;p&gt;So it&amp;rsquo;s naturally that I started to write a tool that generates TS, Rust, and Zig from Go. But the experience is not conclusive&amp;hellip; A lot of language specifics cannot easily be shared by a tool. I&amp;rsquo;m not giving up&amp;hellip; I just have more interesting priorities (yes, I&amp;rsquo;m less bored on GitRoot than at my work ^^). But if this is the kind of thing you like, yes, you, reading that, please reach out to me, and I&amp;rsquo;ll explain all that I did and how you can take it further.&lt;/p&gt;

&lt;p&gt;By the way, I discovered another kind of tool that does a similar job: &lt;a target=&quot;_blank&quot; href=&quot;https://component-model.bytecodealliance.org/design/wit.html&quot;&gt;WIT&lt;/a&gt;. It&amp;rsquo;s not an official spec from the W3C, but since it&amp;rsquo;s from the Bytecode Alliance, it&amp;rsquo;s big enough to use. I have quickly looked at it, and the engine I use for Wasm is not really compatible for now. But I&amp;rsquo;m keeping that in mind; it could be a replacement for &lt;code&gt;SdkGenerator&lt;/code&gt; if nobody (including me) wants to do the job.&lt;/p&gt;

&lt;h3 id=&quot;max-anonymous-branches&quot;&gt;Max anonymous branches&lt;/h3&gt;

&lt;p&gt;When a lot of people started to talk about GitRoot, one thing haunted my nights: what if someone builds a simple script that clones GitRoot, creates 1,000 branches, and pushes them? I would need to &lt;code&gt;git push origin :branch&lt;/code&gt; for every single one of them. You don&amp;rsquo;t need to register to contribute to GitRoot, and that&amp;rsquo;s one of its strengths, but also one of its weaknesses.&lt;/p&gt;

&lt;p&gt;How to make that impossible without blocking all the beauty of GitRoot? One day, a simple solution came to me (yes, I love imagining scenarios running against me). Why not limit the number of branches from anonymous users? It&amp;rsquo;s simple after all: I need to count the number of branches made by anonymous users, and if it exceeds a limit, I stop accepting them. However, people I have added to my repo because of a previous contribution can still push new branches. That is what I added, but&amp;hellip; there is always a but&amp;hellip; counting the number of branches is lightweight, but counting the number of branches from an anonymous user is really expensive. Life is full of choices, and I made one.&lt;/p&gt;

&lt;p&gt;When the branch limit is reached, we stop accepting anonymous contributions. That means if you set 10 as the maximum and you make 20 branches, nobody else can contribute. Yes, it&amp;rsquo;s not perfect, but it&amp;rsquo;s way better than having nightmares every night.&lt;/p&gt;

&lt;p&gt;If an admin configures the forge with &lt;code&gt;NbMaxBranchesPerProjectBeforeRejectingAnonymous=10&lt;/code&gt;, that means when a project has 10 branches (who added them doesn&amp;rsquo;t matter), the next anonymous user (not present in &lt;code&gt;.gitroot/users.yml&lt;/code&gt;) will be rejected. Of course, all known users can continue to create as many branches as they want.&lt;/p&gt;

&lt;p&gt;A project can set a lower number, &lt;code&gt;NbMaxBranchesBeforeRejectingAnonymous=5&lt;/code&gt;, but if the number is bigger, it will have no effect (e.g., if set to &lt;code&gt;NbMaxBranchesBeforeRejectingAnonymous=15&lt;/code&gt;, GitRoot will use 10 as set by the forge administrator).&lt;/p&gt;

&lt;p&gt;A neat trick: setting &lt;code&gt;NbMaxBranchesBeforeRejectingAnonymous=0&lt;/code&gt; blocks all anonymous pushes. Alternatively, you can use the &lt;a href=&quot;doc/how-tos/no_anonymous_user.html&quot;&gt;Not accepting anonymous contribution&lt;/a&gt; guide. The main difference is the error message: the first gives &lt;code&gt;anonymous branches not allowed&lt;/code&gt;, while the latter shows &lt;code&gt;you can&#39;t write in $branchName&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;zig-plugin-sdk&quot;&gt;Zig plugin-sdk&lt;/h3&gt;

&lt;p&gt;With every release, I try to add a new plugin-sdk language. This time I picked &lt;a target=&quot;_blank&quot; href=&quot;https://ziglang.org/&quot;&gt;zig&lt;/a&gt; because it is a language that natively manages Wasm and WASI, so I was pretty sure I would finish this implementation.&lt;/p&gt;

&lt;p&gt;I loved the error system, compared to Rust (optionals) and Go (multiple returns), Zig chose an in-between solution with &lt;code&gt;!&lt;/code&gt; and some language keywords. But what totally blew my mind is the &lt;code&gt;comptime&lt;/code&gt; feature, where the compiler can become an interpreter for compilation output. Amazing idea, I am pretty sure a lot of things will follow this path.&lt;/p&gt;

&lt;p&gt;Long story short, I spent a long time discovering the language, and it was a very good time for me (it changes your perspective, seeing the same problem solved another way)&amp;hellip; but nothing related directly to GitRoot.&lt;/p&gt;

&lt;p&gt;As with all the plugin-sdks, there is unfortunately no way for you to use it yet. I don&amp;rsquo;t release them in registries until the API is stable, and a lot of breaking changes are coming in the next release.&lt;/p&gt;

&lt;h3 id=&quot;pluginsdk-improvements&quot;&gt;PluginSdk improvements&lt;/h3&gt;

&lt;p&gt;Regarding the plugin-sdk, here are a lot of new capabilities for plugins to use:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;schedule&lt;/code&gt;: you can allow a plugin to be called at a scheduled time. The format is a cron expression with 5 segments as &lt;code&gt;&amp;lt;minute&amp;gt; &amp;lt;hour&amp;gt; &amp;lt;day&amp;gt; &amp;lt;month&amp;gt; &amp;lt;weekday&amp;gt;&lt;/code&gt;. Example:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;purl&lt;/span&gt;: pkg:gitroot/gitroot/firefly@0.0.1?checksum=none&amp;amp;registry=file:%2F%2F%2Fhome%2Frmaneschi%2Fprojects%2Fgitroot%2Fapp%2Ftestsuite%2F..%2Fplugins%2Ffirefly
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;active&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;schedule&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;* * * * *&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That means the firefly plugin will be executed on the main branch every minute. Refer to the &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/adhocore/gronx#cron-expression&quot;&gt;gronx library documentation&lt;/a&gt; for cron expression details.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;httpClient&lt;/code&gt;: plugins can make HTTP requests. I have added standard verbs (&lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt; and &lt;code&gt;DELETE&lt;/code&gt;), if you need more, please let me know. You can also add headers as a &lt;code&gt;map[string][]string&lt;/code&gt;. In the next version, I will add permission a plugin must request beforehand.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;callFunc&lt;/code&gt; configuration: you can add a configuration for the called plugin within the caller plugin&amp;rsquo;s &lt;code&gt;callFunc&lt;/code&gt;. This makes it possible to tell the called plugin what its configuration is in this context. Previously, an empty config was set, so the plugin&amp;rsquo;s defaults (if any) were used. Example:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;callFunc&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;pluginPURL&lt;/span&gt;: pkg:gitroot/gitroot/apex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;funcName&lt;/span&gt;: renderHtml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;branchesDir&lt;/span&gt;: branches
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;favicon&lt;/span&gt;: logo.png
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;footer&lt;/span&gt;: &amp;lt;p&amp;gt;Start with &amp;lt;code&amp;gt;git clone ssh://gitroot.dev/&amp;lt;/code&amp;gt; ��&amp;lt;/p&amp;gt;&amp;lt;small&amp;gt;Hosted with ❤️ by Gitroot&amp;lt;/small&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;generateGitWorktree&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt;9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;header&lt;/span&gt;: ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;init&lt;/code&gt; function, one to rule them all: I have refactored the &lt;code&gt;init()&lt;/code&gt; function of the plugin-sdk to provide an &lt;code&gt;initKind model.InitKind&lt;/code&gt;, which can be &lt;code&gt;confhaschange&lt;/code&gt;, &lt;code&gt;diff&lt;/code&gt;, &lt;code&gt;schedule&lt;/code&gt;, or &lt;code&gt;funccall&lt;/code&gt;. This lets the plugin know in which context it is running, and this architecture allows future hooks to be added without breaking the API.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pull-iterator&lt;/code&gt;: I have introduced pull-iterators to be able to group reports without sending all reports to the Wasm memory. In a future version, I will use this mechanism to transfer commits and files directly to Wasm.&lt;/p&gt;

&lt;h3 id=&quot;sftp-feature&quot;&gt;Sftp feature&lt;/h3&gt;

&lt;p&gt;Users can access every repository&amp;rsquo;s web directory through SFTP. To do this, you can use the same SSH key that you use for your Git repository. When you log in with &lt;code&gt;sftp -P $SshPort $SshAddress:/$repo/&lt;/code&gt; (like &lt;code&gt;sftp -P 4545 127.0.0.1:/repo1/&lt;/code&gt;), you will see the list of web representations for your repository. Anyone can do this, but only those in the &amp;ldquo;owner&amp;rdquo; section of &lt;code&gt;.gitroot/users.yml&lt;/code&gt; have write access.&lt;/p&gt;
&lt;blockquote style=&quot;border-color: #006ac7&quot;&gt;

&lt;p&gt;Be careful: this feature is in alpha, so don&amp;rsquo;t write scripts around it for now. I will probably change the directory hierarchy &lt;a href=&quot;issues/d42a-sftp-better-tree.html&quot;&gt;#d42a&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To enable this feature, you need to change &lt;code&gt;activesftp: false&lt;/code&gt; to &lt;code&gt;activesftp: true&lt;/code&gt; in &lt;code&gt;.gitroot/forgeConfig.yml&lt;/code&gt; at the root repository of your instance. Don&amp;rsquo;t forget to restart GitRoot after that, otherwise the parameter will not be taken into account by GitRoot.&lt;/p&gt;

&lt;h3 id=&quot;some-interesting-bug-fixes&quot;&gt;Some interesting bug fixes&lt;/h3&gt;

&lt;p&gt;Of course, a lot of bug fixes have been included in this release. I will present only the most interesting ones, and you can find a more complete list in the &lt;a href=&quot;CHANGELOG.html&quot;&gt;changelog&lt;/a&gt; and an even more thorough one in the commits.&lt;/p&gt;

&lt;p&gt;The first annoying bug occurred when &lt;a href=&quot;plugins/name/grafter.html&quot;&gt;Grafter&lt;/a&gt; committed to a file, but &lt;a href=&quot;plugins/name/apex.html&quot;&gt;Apex&lt;/a&gt; did not render that file if the Grafter plugin was placed after Apex in the configuration. In fact, even if you put Grafter before Apex, the bug persisted because Apex &lt;code&gt;reports&lt;/code&gt; the rendering back to Grafter, meaning it missed that final report. Now, I have added a subsequent loop where plugins can only write to the web directory (and cache) but not to Git. This way, all plugins loop over commits and files to generate new commits, and only afterwards do we loop through all plugins to see if they have anything to write to the web (or cache) excluding Git to avoid an infinite loop.&lt;/p&gt;

&lt;p&gt;Another annoying issue happened when you changed a plugin&amp;rsquo;s configuration, causing it to enter a worktree loop (looping over files in the worktree instead of commits). Consequently, the commits generated by this loop were not propagated to other plugins, causing widespread rendering misses on the web.&lt;/p&gt;

&lt;p&gt;Finally, if you decided to add a plugin multiple times in your &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt; file (to have multiple configurations, for instance), only the first instance was actually loaded and executed. Now, you can include a plugin as many times as you want, with any configuration you choose, and they will all be executed.&lt;/p&gt;

&lt;h2 id=&quot;what-next&quot;&gt;What next?&lt;/h2&gt;

&lt;p&gt;New plugins, bug fixes, updates&amp;hellip; Yes, until now this article was only about GitRoot, but what is GitRoot without its plugins? The immediate next thing I will do is write an article about what new plugin versions can do.&lt;/p&gt;

&lt;p&gt;Then I will probably start working on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discover GitRoot without installing anything.&lt;/li&gt;
&lt;li&gt;Create your own plugins with ease.&lt;/li&gt;
&lt;li&gt;Transform GitRoot into a registry for (at least) Go, Rust, NPM and Zig.&lt;/li&gt;
&lt;li&gt;Federation with GitRoot but also other forges.&lt;/li&gt;
&lt;li&gt;Implement something for users (how to represent them).&lt;/li&gt;
&lt;li&gt;Manage more Git functions in plugins (branch, pull, push, rebase).&lt;/li&gt;
&lt;li&gt;Use pull-iterators to stabilise the plugin-sdk.&lt;/li&gt;
&lt;li&gt;Easier configuration of plugins.&lt;/li&gt;
&lt;li&gt;Auto-update of plugins.&lt;/li&gt;
&lt;li&gt;New language plugin-sdk.&lt;/li&gt;
&lt;li&gt;Air-gapped version of GitRoot.&lt;/li&gt;
&lt;li&gt;Make every repository a gemini capsule.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you compare this with the &lt;a href=&quot;blog/version-0.4.0.html#what-next&quot;&gt;last v0.4 what next&lt;/a&gt; section, there are a lot more things. Every time I finish a feature (or don&amp;rsquo;t, see the &lt;a href=&quot;blog/version-0.5.0.html#sdkgenerator-full-story&quot;&gt;plugin-sdk generator&lt;/a&gt;), I can add ten more new features to do. That&amp;rsquo;s why I love GitRoot, and that&amp;rsquo;s why &lt;code&gt;I Move slowly, and take care of things&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Special thanks to &lt;code&gt;Ryuno-Ki&lt;/code&gt;, &lt;code&gt;Matthieu&lt;/code&gt; and &lt;code&gt;ks&lt;/code&gt; from the &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/#gitroot:matrix.org&quot;&gt;#gitroot:matrix.org&lt;/a&gt; Matrix room for reviewing and correcting this article. Want to help with future posts? Join us in the room!&lt;/em&gt;&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.5.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.4.0.md</link><title>0.4.0</title><pubDate>2026/04/22</pubDate><description>&lt;h1 id=&quot;the-teenager-starts-building-its-own-future&quot;&gt;The teenager starts building its own future&lt;/h1&gt;

&lt;h2 id=&quot;if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes&lt;/h2&gt;

&lt;p&gt;A lot has been done in this new release of GitRoot. When I saw other FOSS projects claiming &amp;ldquo;this is the biggest release&amp;rdquo;, I thought it was a joke. But in fact, it&amp;rsquo;s true: every new release gets bigger and bigger. It&amp;rsquo;s just astonishing to start a sprint with almost zero ideas and, 4 months later, see all of this accomplished.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you skim this article, don&amp;rsquo;t forget to check out the conclusion about &lt;a href=&quot;blog/version-0.4.0.html#what-next&quot;&gt;&amp;ldquo;what&amp;rsquo;s next?&amp;rdquo;&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;the-thing-i-m-most-proud-of&quot;&gt;The thing i&amp;rsquo;m most proud of&lt;/h3&gt;

&lt;p&gt;Executors can finally build GitRoot! As I love to say: GitRoot can build GitRoot on GitRoot. And it was a pain to do. &lt;a href=&quot;blog/version-0.4.0.html#executor&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-thing-with-the-most-visibility&quot;&gt;The thing with the most visibility&lt;/h3&gt;

&lt;p&gt;The plugin registry is here! After a lot of reading and reflecting, I started this &amp;ldquo;epic story&amp;rdquo;. Today is only the beginning, but you can now find plugins directly at &lt;a href=&quot;plugins/index.html&quot;&gt;gitroot.dev/plugins&lt;/a&gt;. What&amp;rsquo;s next? &lt;a href=&quot;blog/version-0.4.0.html#plugin-registry&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-thing-that-changes-everything-but-was-so-simple&quot;&gt;The thing that changes everything but was so simple&lt;/h3&gt;

&lt;p&gt;Previously, a branch (and graft) couldn&amp;rsquo;t be rendered on the web. The problem was: if I have an &lt;code&gt;index.md&lt;/code&gt; in my git that renders at &lt;code&gt;myinstance.com/index.html&lt;/code&gt;, anyone can create a branch. If a &amp;ldquo;bad user&amp;rdquo; comes along, changes everything in this file, creates a branch, and pushes for review, where should this file render? Certainly not at &lt;code&gt;myinstance.com/index.html&lt;/code&gt;. &lt;a href=&quot;blog/version-0.4.0.html#apex-render-branch&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-thing-that-was-more-complex-than-i-thought&quot;&gt;The thing that was more complex than I thought&lt;/h3&gt;

&lt;p&gt;Apex was a big plugin. Basically, it manages the rendering of your git files to the web. But over time, it started to grow out of control because of markdown, code, worktrees, emojis&amp;hellip; A simple (in theory) solution was to split it into multiple plugins. &lt;a href=&quot;blog/version-0.4.0.html#plugin-deps&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-things-i-m-just-happy-are-done&quot;&gt;The things I&amp;rsquo;m just happy are done&lt;/h3&gt;

&lt;p&gt;No more mandatory signed commits! Well, it&amp;rsquo;s still better if you sign them, but this change means the &lt;code&gt;.gitroot/init.sh&lt;/code&gt; has been &lt;del&gt;deleted&lt;/del&gt; moved to a plugin. &lt;a href=&quot;blog/version-0.4.0.html#sigma-plugin&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Moving a file from git to web was hard, deleting a file was &amp;ldquo;not implemented&amp;rdquo;, and a lot of APIs were missing in the plugin-sdk. I got my hands dirty with all that, and now everything is running smoothly. &lt;a href=&quot;blog/version-0.4.0.html#fs-plugin-sdk&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;in-depth-details&quot;&gt;In-depth Details&lt;/h2&gt;

&lt;h3 id=&quot;executor&quot;&gt;Executor&lt;/h3&gt;

&lt;p&gt;When I started this feature, I just wanted a lightweight container to run &lt;code&gt;make build&lt;/code&gt; and output the result as a &lt;code&gt;gitroot-next&lt;/code&gt; canary binary. If I had known how much time and energy it would actually cost, I’m not sure I would have taken this road.&lt;/p&gt;

&lt;p&gt;At first, I chose &lt;a href=&quot;doc/technicals/executors.html#bwrap-executor&quot;&gt;bubblewrap&lt;/a&gt; as a lightweight container, and immediately started building a &lt;a href=&quot;doc/technicals/executors.html#container-executor&quot;&gt;container&lt;/a&gt; version. Then I realized I needed a &lt;a href=&quot;doc/technicals/executors.html#baremetal-executor&quot;&gt;bareMetal&lt;/a&gt; one. And then, I needed a way to run it on other machines, so &lt;a href=&quot;doc/technicals/executors.html#ssh-executor&quot;&gt;ssh&lt;/a&gt; came along.&lt;/p&gt;

&lt;p&gt;But when I launched it for the first time, I discovered that having &lt;code&gt;make&lt;/code&gt; isn&amp;rsquo;t enough for GitRoot, I need Golang, Rust, TinyGo, and so on. &amp;ldquo;No problem,&amp;rdquo; I thought, &amp;ldquo;I&amp;rsquo;ll use &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt; to manage that.&amp;rdquo; But running &lt;code&gt;mise install&lt;/code&gt; takes a lot of time (like 10-15 minutes on my computer). So, I built a cache system to ensure that hit only happens the first time.&lt;/p&gt;

&lt;p&gt;Still not the end! After that, I needed to configure port numbers, so I built a complete environment variable system, and used it to configure some &lt;code&gt;mise&lt;/code&gt; paths.&lt;/p&gt;

&lt;p&gt;Is it finished? Not yet! To be able to move a binary generated from a bwrap environment to the web, it has to travel a lot. And by &amp;ldquo;a lot,&amp;rdquo; I mean moving it to a place where the plugin that launched the executor can access it: its own cache. Then the plugin needs to move it from there to the web. No problem, I just added a bunch of &lt;a href=&quot;blog/modifying-API-SDK-plugin.html&quot;&gt;SDK API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After all that, some commands finally started working&amp;hellip; well, kind of. When I ran a bwrap or a container command, I couldn&amp;rsquo;t get the stats of what was happening inside. Having process stats is something I’m proud of (even if it’s still evolving in future versions), but seeing that bwrap reported 20 MB of RAM while the sub-process was actually consuming 80 MB was very frustrating. Furthermore, I needed a way to extract stats from the remote SSH executor.&lt;/p&gt;

&lt;p&gt;I tried a lot of things, but ultimately, I chose to build an &lt;a href=&quot;app/executor/main.go&quot;&gt;executor CLI&lt;/a&gt; to handle it. I now inject that binary inside your bwrap, container, or SSH environment. This little CLI launches commands, gathers system stats, command status, and logs. It packs everything into a well-formatted JSON and returns it to the executor in GitRoot. GitRoot then manages artifacts/caches and hands the result back to the calling plugin (probably &lt;a href=&quot;plugins/name/hop.html&quot;&gt;hop&lt;/a&gt;), which tells you what’s happening. In this case, it reports back so the &lt;a href=&quot;plugins/name/grafter.html&quot;&gt;grafter plugin&lt;/a&gt; can include the info in the current graft.&lt;/p&gt;
&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;295.9538&quot; height=&quot;429.0792&quot; viewBox=&quot;0 0 295.9538 429.0792&quot;&gt;&lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;295.9538&quot; height=&quot;429.0792&quot; fill=&quot;transparent&quot;/&gt;&lt;defs&gt;&lt;marker id=&quot;arrow-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 10 5 L 0 10 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-start-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;4.5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 5 L 10 10 L 10 0 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;path id=&quot;edge-0&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-0&quot; d=&quot;M 210.589,59.000 L 210.589,107.236&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot; x=&quot;105.97&quot; y=&quot;62.00&quot; width=&quot;90.62&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;151.28&quot; y=&quot;79.70&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;151.28&quot; dy=&quot;0.00&quot;&gt;Exec cmds&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-1&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-1&quot; d=&quot;M 210.679,158.236 L 210.679,206.472&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;path id=&quot;edge-2&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-2&quot; d=&quot;M 210.835,257.472 L 210.835,305.708&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-2&quot; data-label-kind=&quot;center&quot; x=&quot;17.60&quot; y=&quot;260.47&quot; width=&quot;179.24&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-2&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;107.22&quot; y=&quot;278.17&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;107.22&quot; dy=&quot;0.00&quot;&gt;bwrap/container/ssh&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-3&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-3&quot; d=&quot;M 210.955,356.708 L 210.955,370.079&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;path id=&quot;edge-4&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-4&quot; d=&quot;M 166.834,395.579 L 153.002,395.579 L 120.042,347.208 L 133.874,347.208&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;path id=&quot;edge-5&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-5&quot; d=&quot;M 133.874,315.208 L 8.000,315.208 L 8.000,247.972 L 165.077,247.972&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-5&quot; data-label-kind=&quot;center&quot; x=&quot;46.42&quot; y=&quot;258.31&quot; width=&quot;206.12&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-5&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;149.48&quot; y=&quot;276.01&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;149.48&quot; dy=&quot;0.00&quot;&gt;status/code/logs/stats&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-6&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-6&quot; d=&quot;M 165.077,215.972 L 41.355,215.972 L 41.355,148.736 L 153.161,148.736&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-6&quot; data-label-kind=&quot;center&quot; x=&quot;56.06&quot; y=&quot;159.07&quot; width=&quot;206.12&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-6&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;159.12&quot; y=&quot;176.77&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;159.12&quot; dy=&quot;0.00&quot;&gt;status/code/logs/stats&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-7&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-7&quot; d=&quot;M 153.161,116.736 L 26.706,116.736 L 26.706,33.500 L 167.201,33.500&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-7&quot; data-label-kind=&quot;center&quot; x=&quot;57.12&quot; y=&quot;59.83&quot; width=&quot;206.12&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-7&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;160.18&quot; y=&quot;77.53&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;160.18&quot; dy=&quot;0.00&quot;&gt;status/code/logs/stats&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;rect x=&quot;167.20&quot; y=&quot;8.00&quot; width=&quot;86.75&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.58&quot; y=&quot;37.00&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.58&quot; dy=&quot;0.00&quot;&gt;Hop&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;153.16&quot; y=&quot;107.24&quot; width=&quot;114.88&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.60&quot; y=&quot;136.24&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.60&quot; dy=&quot;0.00&quot;&gt;GitRoot&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;133.87&quot; y=&quot;305.71&quot; width=&quot;154.08&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.91&quot; y=&quot;334.71&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.91&quot; dy=&quot;0.00&quot;&gt;executor cli&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;166.83&quot; y=&quot;370.08&quot; width=&quot;88.32&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;211.00&quot; y=&quot;399.08&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;211.00&quot; dy=&quot;0.00&quot;&gt;cmd&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;165.08&quot; y=&quot;206.47&quot; width=&quot;91.36&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.76&quot; y=&quot;235.47&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.76&quot; dy=&quot;0.00&quot;&gt;exec&lt;/tspan&gt;&lt;/text&gt;&lt;g transform=&quot;translate(210.59 107.24) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(210.68 206.47) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(210.84 305.71) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(210.95 370.08) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(133.87 347.21) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(165.08 247.97) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(153.16 148.74) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(167.20 33.50) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;/svg&gt;
&lt;p&gt;Phew, is that enough now? It should be, but we&amp;rsquo;ll see how it performs in real-time on &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev&quot;&gt;gitroot.dev&lt;/a&gt;. Not everything is finished: for example, the SSH executor doesn’t manage cache yet &lt;a href=&quot;issues/94af-executor-cache.html&quot;&gt;#94af&lt;/a&gt; and can&amp;rsquo;t use the container strategy &lt;a href=&quot;issues/f050-ssh-executor-subCmd.html&quot;&gt;#f050&lt;/a&gt;. You can’t run the executor on other architectures &lt;a href=&quot;issues/4d49-executor-compile-other-archs.html&quot;&gt;#4d49&lt;/a&gt; yet, and I’m sure you’ll soon want a way to clean up temp directories &lt;a href=&quot;issues/647f-clean-executor-temp-dir.html&quot;&gt;#647f&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But hey, let’s celebrate what we’ve built before jumping into the next sprint!&lt;/p&gt;

&lt;h3 id=&quot;plugin-registry&quot;&gt;Plugin registry&lt;/h3&gt;

&lt;p&gt;Until now, explaining GitRoot was always about &amp;ldquo;data in git and the plugin system&amp;rdquo;, but what exactly are the plugins? Where are they? What can they do? All of that was a bit mysterious. With this milestone, I’m answering some of those questions with a first shot at a registry. Be careful, though: this first version is just a &amp;ldquo;hand-written&amp;rdquo; preview of what it could be in the future. If you look at the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/plugins/index.md&quot;&gt;source&lt;/a&gt;, you’ll see that everything is ready to be automated, but nothing is&amp;hellip; yet.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;m not entirely sure what you, as a consumer, want to see here. Probably an explanation of what the plugin does, some docs, and the available versions. But as a plugin creator, you likely want to manage all of that in your own repository instead of the &lt;a href=&quot;plugins/index.html&quot;&gt;gitroot.dev registry&lt;/a&gt;. I chose to keep the registry very light and put the details directly in the &lt;a href=&quot;app/plugins/apex/index.html&quot;&gt;plugin&amp;rsquo;s directory&lt;/a&gt; to give you an idea of what your own repo could look like.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ll need to iterate on this. Even if I choose to embed package details in the registry, I still need to decide how: maybe via a JSON in your repo, maybe in the registry itself, or perhaps inside your package (WASM)? I don&amp;rsquo;t know yet what’s simplest for you and for the registry.&lt;/p&gt;

&lt;p&gt;Anyway, I hope this registry helps you understand what a plugin is and how it shapes your forge. Some are small (&lt;a href=&quot;plugins/name/ladybug.html&quot;&gt;ladybug&lt;/a&gt;), others are massive (&lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex&lt;/a&gt;), and some aren&amp;rsquo;t usable solo as they are &lt;a href=&quot;blog/version-0.4.0.html#plugin-deps&quot;&gt;dependencies&lt;/a&gt; for others (like &lt;a href=&quot;plugins/name/apex_mermaid.html&quot;&gt;apx_mermaid&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Since plugins are now decoupled from GitRoot, I’ve moved all changelogs from the &lt;a href=&quot;CHANGELOG.html&quot;&gt;general&lt;/a&gt; one to the individual plugin directories. This way, I’ll be able to release plugin versions independently of GitRoot (and vice versa). I hope to break the plugin API less often, even if I know I probably will in the near future. All of this paves the way for you to build your own plugins and I hope to see them in the gitroot.dev registry soon. Or, of course, you can just build your own registry!&lt;/p&gt;

&lt;h3 id=&quot;apex-render-branch&quot;&gt;Apex render branch&lt;/h3&gt;

&lt;p&gt;For as long as I can remember, this has been a problem with GitRoot: how to render arbitrary changes, especially deletions, on the web? I had imagined a lot of strategies. The last one was to render inside a directory named after the git tree ID (the abstract representation of a working directory). Then, I’d create a symlink between that tree and a main directory.&lt;/p&gt;

&lt;p&gt;The idea was: if you push a file in a branch, git gives you a tree ID like &lt;code&gt;a654fs&lt;/code&gt;. It would be represented as &lt;code&gt;mydomain.com/myBranch/file -&amp;gt; mydomain.com/tree/a654fs/file&lt;/code&gt;. When you merged that branch, GitRoot would simply symlink the main directory to it: &lt;code&gt;mydomain.com/file -&amp;gt; mydomain.com/tree/a654fs/file&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Elegant, but honestly, a total over-engineered mess. When I actually sat down to code this, I ended up adding just two lines to the &lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex plugin&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; p.currentCommit.Branch &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; p.currentCommit.Branch &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; p.config.defaultBranch {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	path = filepath.&lt;span style=&quot;color:#50fa7b&quot;&gt;Join&lt;/span&gt;(p.config.branchesDir, p.currentCommit.Branch, path)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And&amp;hellip; yeah, that was enough. I&amp;rsquo;ve been overthinking this since the very beginning of GitRoot! Rendering files from branches into a dedicated branches directory&amp;hellip; It&amp;rsquo;s simple. Just like GitRoot should be.&lt;/p&gt;

&lt;p&gt;So, what does it mean in practice? The file is now rendered at &lt;code&gt;myinstance.com/mybranch/index.html&lt;/code&gt; and the graft at &lt;code&gt;myinstance.com/mybranch/grafts/mybranch.html&lt;/code&gt;. How did I not think of something this simple before?&lt;/p&gt;

&lt;h3 id=&quot;plugin-deps&quot;&gt;Plugin deps&lt;/h3&gt;

&lt;p&gt;The catalyst for this development was the &lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex plugin&lt;/a&gt;. At over 3 MB, it was by far the largest plugin. It was packed with configuration, specifically a bunch of booleans to &lt;a href=&quot;issues/8b29-apex-working-tree.html&quot;&gt;toggle features&lt;/a&gt;. Furthermore, I needed to render Markdown in other plugins, like &lt;a href=&quot;plugins/name/pollen.html&quot;&gt;pollen&lt;/a&gt;, to create a proper &lt;a href=&quot;issues/83d8-pollen-content-flux.html&quot;&gt;RSS feed&lt;/a&gt;. It became clear to me: this plugin needed to be split into smaller, more specialized ones.&lt;/p&gt;

&lt;p&gt;So, I started building the &lt;a href=&quot;plugins/name/apex_markdown.html&quot;&gt;apex_markdown plugin&lt;/a&gt;, then &lt;a href=&quot;plugins/name/apex_code.html&quot;&gt;apex_code&lt;/a&gt;, and next week &lt;a href=&quot;plugins/name/apex_mermaid.html&quot;&gt;apex_mermaid&lt;/a&gt;. But what I hadn&amp;rsquo;t fully anticipated was the sheer amount of work required to make it all work together.&lt;/p&gt;

&lt;p&gt;What if a dependency is missing? What if a method is deprecated or removed in a newer version? To handle this, I’ve chosen to make all dependencies entirely optional for now. It’s an easy choice to make today, but I might change my mind tomorrow, after all, there’s a reason most package managers handle version ranges and resolution.&lt;/p&gt;

&lt;p&gt;This new architecture allows for adding tons of features without rebuilding an entire plugin. Imagine adding Org-mode or RST support: you just swap out &lt;a href=&quot;plugins/name/apex_markdown.html&quot;&gt;apex_mardown&lt;/a&gt; instead of touching the whole stack. It also allows other plugins to reuse existing behaviors effortlessly.&lt;/p&gt;

&lt;p&gt;With this in place, I’m not even sure if I’ll keep the &lt;code&gt;report&lt;/code&gt; system, which was an other way of passing information between plugins.&lt;/p&gt;

&lt;h3 id=&quot;sigma-plugin&quot;&gt;Sigma plugin&lt;/h3&gt;

&lt;p&gt;Signed commits should be the default in git today. With SSH signing, it’s easy to do, and everyone should use it because impersonating a name and email is way too easy in git. When I started GitRoot, I made signed commits mandatory before allowing a push. But in fact, making signatures mandatory should be a choice for the instance owner. GitRoot is all about the freedom to choose how your forge works for you.&lt;/p&gt;

&lt;p&gt;Furthermore, a friend told me he preferred signing with PGP and already had his setup ready. So why force him to use a &lt;code&gt;.gitroot/init.sh&lt;/code&gt;? It’s frustrating when you want to test a new forge and you’re forced to run random sh commands, even if it&amp;rsquo;s just to configure your git client.&lt;/p&gt;

&lt;p&gt;With this release, I have completely removed &lt;code&gt;init.sh&lt;/code&gt; from GitRoot itself. But of course, as always, I’ve created a plugin to &amp;ldquo;almost&amp;rdquo; recreate this feature. The only difference is: instead of refusing your commit when you push, the &lt;a href=&quot;plugins/name/stigma.html&quot;&gt;stigma plugin&lt;/a&gt; will simply report unsigned or badly signed commits in your graft.&lt;/p&gt;

&lt;p&gt;Now, you have the choice to accept unsigned commits in your repo. You can even ignore the whole signing topic entirely if you don&amp;rsquo;t install the stigma plugin.&lt;/p&gt;

&lt;p&gt;That said, I’m still convinced that signing commits should remain the default.&lt;/p&gt;

&lt;h3 id=&quot;fs-plugin-sdk&quot;&gt;FS plugin-sdk&lt;/h3&gt;

&lt;p&gt;After finishing all the &lt;a href=&quot;blog/version-0.4.0.html#executor&quot;&gt;executors features&lt;/a&gt;, one thing was still missing: the ability for a plugin to move a file from one path to another. As explained in this &lt;a href=&quot;blog/modifying-API-SDK-plugin.html&quot;&gt;blog post&lt;/a&gt;, even though a plugin sees one single filesystem, GitRoot actually uses three different ones: one for cache (a directory), one for the web (a subdirectory), and a virtual one for the git working directory.&lt;/p&gt;

&lt;p&gt;I was missing several methods in both the Rust and TS SDKs, and the existing API was poorly designed with inconsistent naming. I decided to redesign everything and added a lot of new methods accessible via &lt;code&gt;FS&lt;/code&gt; in the SDK. Now, you can copy, move, delete, replace, and write files in every filesystem. Moving and copying can now be done between different filesystems, making it easy to make a file accessible on the web even if it originated from git or the cache.&lt;/p&gt;

&lt;p&gt;I have marked all old methods as deprecated, and they will be removed in the next version of GitRoot (or more specifically, the plugin-sdk, though they share the same release number for now).&lt;/p&gt;

&lt;h2 id=&quot;what-next&quot;&gt;What next?&lt;/h2&gt;

&lt;p&gt;I have a massive roadmap for GitRoot (in no particular order):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discover GitRoot without installing anything.&lt;/li&gt;
&lt;li&gt;Create your own plugins with ease.&lt;/li&gt;
&lt;li&gt;Transform GitRoot into a registry for (at least) Go, Rust, and NPM.&lt;/li&gt;
&lt;li&gt;Plugins should be usable via PURL.&lt;/li&gt;
&lt;li&gt;Plugins can send/receive HTTP requests.&lt;/li&gt;
&lt;li&gt;Plugins can be scheduled.&lt;/li&gt;
&lt;li&gt;Rebase go-git to take advantage of their performance improvements.&lt;/li&gt;
&lt;li&gt;Generate the plugin SDK automatically.&lt;/li&gt;
&lt;li&gt;Add plugin SDKs for (at least) Zig, Kotlin, and Python.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yeah, I could go on all night. And those are just core GitRoot features. I haven&amp;rsquo;t even mentioned making instances to build projects in hosted environments. I haven&amp;rsquo;t talked about specific plugins (sending a &amp;ldquo;toot&amp;rdquo; on commit, managing secrets with age, translating your app&amp;hellip;). I haven&amp;rsquo;t even touched on federating GitRoot with other GitRoot instances, or with Forgejo, SourceHut, or Radicle.&lt;/p&gt;

&lt;p&gt;All of that is possible, and I’m confident it will be accessible in the future. But &amp;ldquo;when&amp;rdquo;? That’s the real question. If you think you can help in any way (coding, of course, but also management, communication, or just to brainstorm), please &lt;a href=&quot;contact.html&quot;&gt;let me know&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is only the beginning. GitRoot will get better and better with every release.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.4.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.3.0.md</link><title>0.3.0</title><pubDate>2025/12/20</pubDate><description>&lt;h1 id=&quot;from-baby-to-teenager-gitroot-become-a-forge&quot;&gt;From &amp;ldquo;baby to teenager&amp;rdquo; GitRoot become a forge&lt;/h1&gt;

&lt;h2 id=&quot;if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes&lt;/h2&gt;

&lt;p&gt;Previously, GitRoot served as a &lt;strong&gt;Source Code Management (SCM)&lt;/strong&gt; system, allowing you to create Git repositories and manage access permissions. However, GitRoot was never intended to be &lt;em&gt;just&lt;/em&gt; an SCM. With this &lt;strong&gt;v0.3.0&lt;/strong&gt; release, it evolves into a full-fledged &lt;strong&gt;forge&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To activate this new capability on your instance, &lt;a href=&quot;blog/version-0.3.0.html#for-admin-activating-an-executor&quot;&gt;follow the administrator section&lt;/a&gt;. To start using it as a user, &lt;a href=&quot;blog/version-0.3.0.html#for-user-using-the-hop-plugin&quot;&gt;see the user section&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The introduction of &lt;strong&gt;executors&lt;/strong&gt; brings a crucial new feature: the &lt;a href=&quot;blog/version-0.3.0.html#plugin-api-report-and-reporter&quot;&gt;report and reporter plugin API&lt;/a&gt;. This API allows any plugin to provide informational feedback to the user. &lt;strong&gt;Important:&lt;/strong&gt; avoid reporting low-level technical details here (use the &lt;code&gt;log&lt;/code&gt; and &lt;code&gt;logError&lt;/code&gt; APIs for that). The initial implementation uses this feature to display command statistics (CPU, memory, etc.) from the new &lt;code&gt;hop&lt;/code&gt; plugin in a clean table via the &lt;code&gt;grafter&lt;/code&gt; plugin.&lt;/p&gt;

&lt;p&gt;With the new &lt;code&gt;hop&lt;/code&gt; plugin, GitRoot now supports 3 languages (Go, TypeScript, Rust) across 4 compilers (Go, TinyGo, Rust, AssemblyScript). Setting up a development environment for this polyglot stack is complex, and keeping it updated is even harder, unless you have the right tools. For this iteration, I have selected &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt; as the default tool for &lt;a href=&quot;blog/version-0.3.0.html#gitroot-bets-on-mise&quot;&gt;managing the GitRoot dev environment&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Beyond technical features, a new focus has been placed on &lt;a href=&quot;blog/version-0.3.0.html#communication-and-community-building&quot;&gt;communication&lt;/a&gt;. You can now follow daily progress on our new fediverse instance (@forge@gitroot.dev), engage in direct chat on matrix (#gitroot:matrix.org), and read a new blog post detailing concrete ways to contribute today.&lt;/p&gt;

&lt;p&gt;Finally, I discuss &lt;a href=&quot;blog/version-0.3.0.html#apex-a-better-web-experience&quot;&gt;Apex&lt;/a&gt; and many &lt;a href=&quot;blog/version-0.3.0.html#and-others-useful-updates&quot;&gt;other tiny but useful things&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;in-depth-details&quot;&gt;In-depth Details&lt;/h2&gt;

&lt;h3 id=&quot;for-admin-activating-an-executor&quot;&gt;For admin: activating an executor&lt;/h3&gt;

&lt;p&gt;When you update GitRoot to &lt;code&gt;0.3.0&lt;/code&gt; a new configuration block will appear in the &lt;code&gt;.gitroot/forgeConfig.yml&lt;/code&gt; file of your &lt;code&gt;root&lt;/code&gt; repository:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;execconf&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;baremetal&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;bwrap&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;user&lt;/span&gt;: nobody
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;uid&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;65534&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;gid&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;65534&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;robind&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;bind&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;container&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;bin&lt;/span&gt;: podman
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;ssh&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;hosts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;user&lt;/span&gt;: myUSer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;address&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;127.0.0.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;port&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;22&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;publickey&lt;/span&gt;: ssh-ed25519 AAAAC3NzaC1...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can enable multiple executors, but GitRoot will only use the first one enabled, checking them in this order: &lt;code&gt;bareMetal&lt;/code&gt;, &lt;code&gt;bwrap&lt;/code&gt;, &lt;code&gt;container&lt;/code&gt; and then &lt;code&gt;ssh&lt;/code&gt;. If you activate an executor, remember to also add the &lt;code&gt;hop&lt;/code&gt; plugin, as it is the only known plugin designed to utilize them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;bare-metal-simplest-but-most-dangerous&quot;&gt;Bare Metal: simplest but most dangerous&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;bareMetal&lt;/code&gt; executor runs commands directly on the host machine. It requires no external dependencies and should work on any hardware. Be extremely cautious: users of your GitRoot instance will gain the ability to execute arbitrary code on the host.&lt;/p&gt;

&lt;h4 id=&quot;bubblewrap-bwrap-lightweight-sandboxing&quot;&gt;Bubblewrap (bwrap): lightweight sandboxing&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;bwrap&lt;/code&gt; executor introduces a lightweight container (sandbox) between executed commands and the host, similar to the technology used by &lt;a target=&quot;_blank&quot; href=&quot;https://flatpak.org/&quot;&gt;flatpack&lt;/a&gt;. To use it, you must install the &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/containers/bubblewrap&quot;&gt;bubblewrap&lt;/a&gt; application.&lt;/p&gt;

&lt;p&gt;By default, GitRoot configures &lt;code&gt;bwrap&lt;/code&gt; to use the &lt;code&gt;nobody&lt;/code&gt; user for restriction, though this can be changed. You can also define bindings between your host and the commands running inside the container. For example, &lt;code&gt;robind: [&amp;quot;/var/log/caddy/access.log&amp;quot;]&lt;/code&gt; would allow a job to execute &lt;code&gt;cat /var/log/caddy/access.log&lt;/code&gt;.&lt;/p&gt;

&lt;h4 id=&quot;container-heavyweight-but-most-flexible&quot;&gt;Container: heavyweight but most flexible&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;container&lt;/code&gt; executor enables the execution of commands within &lt;a target=&quot;_blank&quot; href=&quot;https://podman.io/&quot;&gt;podman&lt;/a&gt; or &lt;a target=&quot;_blank&quot; href=&quot;https://www.docker.com/&quot;&gt;docker&lt;/a&gt; containers, allowing your users to run any OCI image. The only requirement for the administrator is to install either &lt;a target=&quot;_blank&quot; href=&quot;https://podman.io/docs/installation&quot;&gt;podman&lt;/a&gt; or &lt;a target=&quot;_blank&quot; href=&quot;https://www.docker.com/&quot;&gt;docker&lt;/a&gt;. Remember to change the &lt;code&gt;bin&lt;/code&gt; property to &lt;code&gt;docker&lt;/code&gt; if that is your chosen runtime.&lt;/p&gt;

&lt;p&gt;GitRoot includes a useful feature for containers: if the user provides a path to a &lt;code&gt;Containerfile&lt;/code&gt; or &lt;code&gt;Dockerfile&lt;/code&gt;, GitRoot will automatically build the image before running commands inside it.&lt;/p&gt;

&lt;h4 id=&quot;ssh-remote-execution-alpha&quot;&gt;SSH: remote execution (alpha)&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;ssh&lt;/code&gt; executor allows commands to be executed on a remote host via a standard SSH connection. You simply provide the standard SSH connection details. This executor should be considered the most alpha of all options (and yes, all others are also considered alpha).&lt;/p&gt;

&lt;h3 id=&quot;for-user-using-the-hop-plugin&quot;&gt;For user: using the &lt;code&gt;hop&lt;/code&gt; plugin&lt;/h3&gt;

&lt;p&gt;To execute a build (or command execution), users must activate the &lt;code&gt;hop&lt;/code&gt; plugin and configure its execution rules:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;url&lt;/span&gt;: /home/rmaneschi/projects/gitroot/app/testsuite/../plugins/hop/hop-0.0.1.wasm
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: hop
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;version&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;0.0.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;active&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          - &lt;span style=&quot;color:#ff79c6&quot;&gt;command&lt;/span&gt;: cat
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          &lt;span style=&quot;color:#ff79c6&quot;&gt;build&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          &lt;span style=&quot;color:#ff79c6&quot;&gt;cmds&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            - &lt;span style=&quot;color:#ff79c6&quot;&gt;args&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;                - README.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;              &lt;span style=&quot;color:#ff79c6&quot;&gt;cmd&lt;/span&gt;: cat
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          &lt;span style=&quot;color:#ff79c6&quot;&gt;env&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add all desired commands for the plugin to call in &lt;code&gt;run.write.exec&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then in &lt;code&gt;run.configuration.exec&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;set environment variables via &lt;code&gt;env&lt;/code&gt; (e.g., &lt;code&gt;&amp;quot;MY_ENV=my_val&amp;quot;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;specify the &lt;code&gt;build&lt;/code&gt; property (only for the &lt;code&gt;container&lt;/code&gt; executor) to use an existing image (e.g., &lt;code&gt;docker.io/bash:5.3.3&lt;/code&gt;) or to build one from a file (e.g., &lt;code&gt;/my/path/to/Containerfile&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;define the commands and arguments to execute under &lt;code&gt;cmds&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;plugin-api-report-and-reporter&quot;&gt;Plugin api report and reporter&lt;/h3&gt;

&lt;p&gt;Occasionally, plugins need to communicate important information to the user. For instance, the &lt;code&gt;ladybug&lt;/code&gt; plugin might need to report invalid metadata. In traditional forges, a web interface serves this purpose, but GitRoot operates without a fixed UI.&lt;/p&gt;

&lt;p&gt;Past considerations included sending emails (a solution often disliked by developers) or leveraging external communication methods like RSS via the &lt;code&gt;pollen&lt;/code&gt; plugin (introduced in v0.2.0) or the Fediverse via a Go-to-Social instance. As always, GitRoot aims to provide options rather than dictate a single solution.&lt;/p&gt;

&lt;p&gt;To allow reporting without forcing a communication channel, I&amp;rsquo;ve created two new plugin APIs.&lt;/p&gt;

&lt;p&gt;Report: plugins call this API with a severity level (&lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, or &lt;code&gt;error&lt;/code&gt;) and an array of strings to display. GitRoot stacks all reports for every commit and plugin.&lt;/p&gt;

&lt;p&gt;Reporter API: after reports are collected, GitRoot calls all plugins that implement the &lt;code&gt;Reporter&lt;/code&gt; interface.&lt;/p&gt;

&lt;p&gt;Currently, the &lt;code&gt;grafter&lt;/code&gt; plugin is the only known reporter; it adds the collected reports to the graft output. This two new API opens up a wide range of possibilities for future versions. I have several ideas, but I encourage you to &lt;a href=&quot;contact.html&quot;&gt;share yours&lt;/a&gt;!&lt;/p&gt;

&lt;h3 id=&quot;gitroot-bets-on-mise&quot;&gt;GitRoot bets on mise&lt;/h3&gt;

&lt;p&gt;Managing a diverse development environment that spans Go, TypeScript, and Rust can be a significant hurdle. In this iteration, I have standardized on mise as the official tool for managing the GitRoot dev environment. This choice simplifies installation and ensures all dependencies remain correctly updated for development.&lt;/p&gt;

&lt;p&gt;This choice seems to be a good one, as we welcome a new contributor who has already integrated some common tasks using &lt;code&gt;mise&lt;/code&gt;. Thanks to their efforts, we can now simply run &lt;code&gt;mise run build&lt;/code&gt; to compile GitRoot or &lt;code&gt;mise run test&lt;/code&gt; to execute all tests.&lt;/p&gt;

&lt;h3 id=&quot;communication-and-community-building&quot;&gt;Communication and community building&lt;/h3&gt;

&lt;p&gt;Alongside the technical work for v0.3.0, a focused effort was put into non-technical tasks to improve communication with the community.&lt;/p&gt;

&lt;p&gt;To allow you to follow daily progress and current thoughts, I have set up a &lt;a target=&quot;_blank&quot; href=&quot;https://gotosocial.org/&quot;&gt;GoToSocial&lt;/a&gt; (Mastodon) instance. You can find the official account at &lt;code&gt;@forge@gitroot.dev&lt;/code&gt; or directly at &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev&quot;&gt;https://gts.gitroot.dev&lt;/a&gt;. This is the best place for real-time updates.&lt;/p&gt;

&lt;p&gt;I also established a direct chat room on &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.org/&quot;&gt;matrix&lt;/a&gt;, accessible via &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/#gitroot:matrix.org&quot;&gt;https://matrix.to/#/#gitroot:matrix.org&lt;/a&gt;. Feel free to stop by to ask questions or just say hello.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ve added a &lt;a href=&quot;contact.html&quot;&gt;contact&lt;/a&gt; page to ensure you can easily find a way to reach me. It includes multiple options such as email, mastodon, or matrix for direct chat. Please don&amp;rsquo;t hesitate to come say &amp;ldquo;hi&amp;rdquo;!&lt;/p&gt;

&lt;p&gt;Finally, I published a blog post titled &lt;a href=&quot;blog/how-to-help_en_11-2025.html&quot;&gt;&amp;ldquo;How to help building GitRoot 11-2025&amp;rdquo;&lt;/a&gt;. The purpose of this post is to present you with concrete, immediate contribution opportunities, rather than speculating on future possibilities. If you wish to get involved, this is the place to start.&lt;/p&gt;

&lt;h3 id=&quot;apex-a-better-web-experience&quot;&gt;Apex a better web experience&lt;/h3&gt;

&lt;p&gt;The Apex plugin receives numerous bug fixes and quality of life improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;favicon support: a favicon can now be added via the configuration&lt;/li&gt;
&lt;li&gt;styled 404 page: the 404 error page now correctly uses the site&amp;rsquo;s CSS&lt;/li&gt;
&lt;li&gt;HTML metadata configuration: I have added configuration options for HTML metadata, which is useful for SEO and for including specific tags like &lt;code&gt;og&lt;/code&gt; (Open Graph) or &lt;code&gt;fediverse&lt;/code&gt; metadata&lt;/li&gt;
&lt;li&gt;menu highlighting: the current page in the navigation menu is now highlighted&lt;/li&gt;
&lt;li&gt;lightweight template: added some variables in header and footer templates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Breaking:&lt;/strong&gt; previously, the &lt;code&gt;apex&lt;/code&gt; plugin rendered markdown metadata as small blocks at the top of the page by default. With this version, it will no longer render this metadata unless the markdown page explicitly includes &lt;code&gt;apexRenderMetadatas: true&lt;/code&gt; in its front matter.&lt;/p&gt;

&lt;h3 id=&quot;and-others-useful-updates&quot;&gt;And others useful updates&lt;/h3&gt;

&lt;p&gt;Although this article is already quite long, I want to briefly mention several smaller yet important tasks completed in this release.&lt;/p&gt;

&lt;h4 id=&quot;forgeconf-update&quot;&gt;ForgeConf update&lt;/h4&gt;

&lt;p&gt;The parameters &lt;code&gt;externalsshaddr&lt;/code&gt; (default value &lt;code&gt;ssh://localhost:4545/&lt;/code&gt;) and &lt;code&gt;externalhttpaddr&lt;/code&gt; (default value &lt;code&gt;http://localhost:4546/&lt;/code&gt;) have been added to ensure the display of correct external urls to users. These addresses are notably used by the &lt;code&gt;apex&lt;/code&gt; plugin generate proper public links. They are kept separate from the internal &lt;code&gt;sshaddr&lt;/code&gt; and &lt;code&gt;httpaddr&lt;/code&gt; because your GitRoot instance might be configured to listen internally (on &lt;code&gt;127.0.0.1&lt;/code&gt;) while being reachable externally through a reverse proxy.&lt;/p&gt;

&lt;h4 id=&quot;integrated-updater&quot;&gt;Integrated updater&lt;/h4&gt;

&lt;p&gt;I&amp;rsquo;ve added a preliminary implementation for an integrated updater. This feature will handle breaking changes gracefully. For this release, I&amp;rsquo;ve added some new parameters to &lt;code&gt;forgeConf.yml&lt;/code&gt;, the integrated updater will handle these by setting standard values that should not affect your current user instance setup.&lt;/p&gt;

&lt;h4 id=&quot;rust-and-wasm&quot;&gt;Rust and WASM&lt;/h4&gt;

&lt;p&gt;While integrating Rust, I discovered that the WASM (and specifically WASI Preview 1) target does not permit exporting the standard &lt;code&gt;free&lt;/code&gt; and &lt;code&gt;malloc&lt;/code&gt; functions, which GitRoot requires. I&amp;rsquo;ve added an option to rename these functions to &lt;code&gt;gitrootAlloc&lt;/code&gt; and &lt;code&gt;gitrootFree&lt;/code&gt;, resolving this compatibility issue.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.3.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.2.0.md</link><title>0.2.0</title><pubDate>2025/09/18</pubDate><description>&lt;h1 id=&quot;version-0-2-0-is-here&quot;&gt;Version 0.2.0 is here&lt;/h1&gt;

&lt;p&gt;In this version, my main focus was on stabilization. I fixed over 14 issues, some of which were identified before version 0.1.0, but a significant proportion were reported by YOU. A big thanks to everyone who took part of it in any way.&lt;/p&gt;

&lt;p&gt;Every time I shared gitroot, I came across new issues with the documentation and binaries, but also questions such as &amp;ldquo;how can I compile it?&amp;rdquo; or &amp;ldquo;what&amp;rsquo;s the next step?&amp;rdquo;. And I&amp;rsquo;m delighted to see your enthusiasm for it!&lt;/p&gt;

&lt;h2 id=&quot;first-contribution&quot;&gt;First contribution&lt;/h2&gt;

&lt;p&gt;Although all my new contributions were related to &amp;ldquo;bug fixes&amp;rdquo;, some of them required new features.&lt;/p&gt;

&lt;h3 id=&quot;no-notification-no-contribution&quot;&gt;No notification == no contribution&lt;/h3&gt;

&lt;p&gt;The first amusing problem came from the first contributor! Fifteen days later, he contacted me to say, &amp;ldquo;Hi, I pushed a branch where I corrected a lot of typos, but&amp;hellip; nothing has happened since I pushed it?&amp;rdquo;. In fact, I wasn&amp;rsquo;t actually aware that a new branch had been pushed. I had forgotten to implement a mechanism that would notify me whenever a new contribution is published!&lt;/p&gt;

&lt;p&gt;A simple way to achieve this is to add an RSS feed (yes, I was inspired by Radicle for this feature). This is ideal, because all I have to do is add an &amp;ldquo;rss.xml&amp;rdquo; file to the web folder&amp;hellip;and the current API of plugin 0.1.0 allows me to do this! So I created the &lt;code&gt;pollen&lt;/code&gt; plugin. It takes all the commits from all branches and adds an &lt;code&gt;rss:item&lt;/code&gt; to a page.&lt;/p&gt;

&lt;p&gt;However, in order to achieve this, I opted to experiment with a different programming language (all the plugins are developed in Golang). I chose &lt;code&gt;assemblyscript&lt;/code&gt;, which is a subset of the &lt;code&gt;typescript&lt;/code&gt; language. So I added a new &lt;code&gt;plugin sdk&lt;/code&gt; in typescript.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To follow all gitroot commits follow &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/rss/all-commits.xml&quot;&gt;https://gitroot.dev/rss/all-commits.xml&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;lot-of-bugs&quot;&gt;Lot of bugs&lt;/h3&gt;

&lt;p&gt;During this first contribution, I discovered numerous bugs in the “grafter” plugin. From the &lt;a href=&quot;issues/close/v0.2.0/73e8-graft-diff-not-working.html&quot;&gt;diff rendering&lt;/a&gt; to &lt;a href=&quot;issues/close/v0.2.0/e188-graft-merge-not-wanted.html&quot;&gt;unwanted merging&lt;/a&gt; and &lt;a href=&quot;issues/close/9422-graft-need-3-review.html&quot;&gt;very complicated final merging&lt;/a&gt;, nothing worked! Even the basic GitRoot features were painful to use :D&lt;/p&gt;

&lt;h2 id=&quot;let-s-the-show-continue&quot;&gt;Let&amp;rsquo;s the show continue&lt;/h2&gt;

&lt;h3 id=&quot;no-linux-no-gitroot&quot;&gt;No linux == no gitroot&lt;/h3&gt;

&lt;p&gt;Surprisingly, someone wanted to compile gitroot on a Mac. Well, gitroot is actually written in Go, so there&amp;rsquo;s no problem doing that&amp;hellip; Except that I forgot to publish a fix I made to a dependency. Go couldn&amp;rsquo;t download the right dependency and therefore fails to compile completely&amp;hellip; But everything is OK now! I added an automatic compilation for Mac, you can find this on the &lt;a href=&quot;CHANGELOG.html&quot;&gt;version&lt;/a&gt; page.&lt;/p&gt;

&lt;h3 id=&quot;doc-doc-doc&quot;&gt;Doc, doc&amp;hellip; doc?&lt;/h3&gt;

&lt;p&gt;After that, someone tried to run gitroot on their local computer, as described in the &lt;a href=&quot;doc/tutorials/from_zero_to_hero.html&quot;&gt;document&lt;/a&gt;. However, when version &lt;code&gt;0.1.0&lt;/code&gt; was released, I discovered a bug. Gitroot cannot know where the &lt;code&gt;root&lt;/code&gt; repository is located (because you can change its name). To fix this, I added a configuration file before initialization, which had to be passed on launch&amp;hellip; It&amp;rsquo;s my fault, I forgot to update this tutorial.&lt;/p&gt;

&lt;h3 id=&quot;a-lot-more&quot;&gt;A lot more&lt;/h3&gt;

&lt;p&gt;As you can see, there were many mistakes in the first version. However, they have all been fixed, and, of course, I have added plenty more to keep me busy for the next version!&lt;/p&gt;

&lt;h3 id=&quot;more-technicals-bugs&quot;&gt;More technicals bugs&lt;/h3&gt;

&lt;p&gt;But I also fixed several important issues that were the result of some shortcuts that every project has to take for publication. One was about a &lt;a href=&quot;issues/close/v0.2.0/95b0-plugins-fs-bug.html&quot;&gt;FS bug&lt;/a&gt; when directories were empty, another concerned the &lt;a href=&quot;issues/close/v0.2.0/f1ab-garbage-collecte-plugins.html&quot;&gt;garbage collect plugin&lt;/a&gt; when the plugin had finished running, and many others.&lt;/p&gt;

&lt;h2 id=&quot;simplify-plugin-install&quot;&gt;Simplify plugin install&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;0.1.0&lt;/code&gt; version, to install a plugin you should download it, tell to gitroot where it is and then after push/pull activate/configure it. Now it is way more simple, just give an url, like &lt;code&gt;https://gitroot.dev/releases/0.2.0/pollen-0.0.1.wasm&lt;/code&gt; and optionnally (but encouraged) a checksum &lt;code&gt;sha256: 49cd8e1846b1612a0d5686918b502fe29e070e7bf9613c1ba9a1dfe5417d0f2a&lt;/code&gt;. Gitroot will download, check and run it. Of course you need to activate it. All url and checksum are in the &lt;a href=&quot;CHANGELOG.html&quot;&gt;versions page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have added a mecanisme to check that the plugin you try to run is compatble with your version of GitRoot. So launching &lt;code&gt;ladybug-0.0.1.wasm&lt;/code&gt; in &lt;code&gt;gitroot-0.2.0&lt;/code&gt; will log an error and not run the wasm. As GitRoot is in alpha version all new gitroot version will need plugins update.&lt;/p&gt;

&lt;h2 id=&quot;what-next&quot;&gt;What next?&lt;/h2&gt;

&lt;p&gt;With the exception of any new bugs detected for the next iteration, I would like to focus on the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;creating a plugins marketplace&lt;/li&gt;
&lt;li&gt;adding a new language SDK for plugins (Maybe Rust? Or Zig?)&lt;/li&gt;
&lt;li&gt;rebasing the Git lib fork and applying patches&lt;/li&gt;
&lt;/ul&gt;
</description><guid>https://gitroot.dev//blog/version-0.2.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.1.0.md</link><title>0.1.0</title><pubDate>2025/09/18</pubDate><description>&lt;h1 id=&quot;gitroot-0-1-0-is-deployed&quot;&gt;GitRoot 0.1.0 is deployed 🎉��&lt;/h1&gt;

&lt;h2 id=&quot;gitroot-in-numbers&quot;&gt;GitRoot in numbers:&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;58 commits&lt;/li&gt;
&lt;li&gt;+500 files &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/&quot;&gt;https://gitroot.dev/worktree/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;1 issue in gitlab (closed) &lt;a target=&quot;_blank&quot; href=&quot;https://gitlab.com/gitroot/gitroot/-/issues/1&quot;&gt;https://gitlab.com/gitroot/gitroot/-/issues/1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;29 issues close &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/boards/closed.html&quot;&gt;https://gitroot.dev/boards/closed.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;43 issues open &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/boards/issues.html&quot;&gt;https://gitroot.dev/boards/issues.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;22 pages de doc &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/doc/&quot;&gt;https://gitroot.dev/doc/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;4 licences &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/README.html#license&quot;&gt;https://gitroot.dev/README.html#license&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;4 plugins &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/CHANGELOG.html#plugins&quot;&gt;https://gitroot.dev/CHANGELOG.html#plugins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But if you retain only one, it is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 contributor &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/.gitroot/users.yml.html&quot;&gt;https://gitroot.dev/worktree/.gitroot/users.yml.html&lt;/a&gt; (others are forge and plugins 😭)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So there is only one thig to do&amp;hellip; read the doc and contribute!&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.1.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/security-vulnerability-1.md</link><title>blog/security-vulnerability-1.md</title><pubDate>2026/03/30</pubDate><description>&lt;h1 id=&quot;security-advisory-executor-command-injection&quot;&gt;Security Advisory: Executor Command Injection&lt;/h1&gt;

&lt;h2 id=&quot;1-executive-summary&quot;&gt;1. Executive Summary&lt;/h2&gt;

&lt;p&gt;A vulnerability has been identified within the GitRoot &lt;strong&gt;executors&lt;/strong&gt; system. Under specific conditions, it is possible for a malicious actor to execute unauthorized commands on the host machine. Given the complexity of the required conditions, this vulnerability is not easily exploitable in a standard production environment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Severity:&lt;/strong&gt; Moderate&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Affected Version:&lt;/strong&gt; 0.3.0&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fix Status:&lt;/strong&gt; Scheduled for version 0.4.0&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;2-technical-details&quot;&gt;2. Technical Details&lt;/h2&gt;

&lt;p&gt;The vulnerability stems from the way executors validate commands defined by plugins. Currently, a plugin can declare a benign command in its manifest (e.g., &lt;code&gt;cat&lt;/code&gt;), but execute an arbitrary command during runtime (e.g., &lt;code&gt;rm -rf data/&lt;/code&gt;). GitRoot currently lacks a strict enforcement mechanism to verify that the executed binary matches the declared command.&lt;/p&gt;

&lt;h2 id=&quot;3-impact-analysis&quot;&gt;3. Impact Analysis&lt;/h2&gt;

&lt;p&gt;The level of risk depends on your infrastructure configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;High Risk:&lt;/strong&gt; Users utilizing &lt;code&gt;bareMetal&lt;/code&gt; or &lt;code&gt;ssh&lt;/code&gt; executors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Low Risk:&lt;/strong&gt; Users utilizing &lt;code&gt;bubblewrap&lt;/code&gt; or &lt;code&gt;container&lt;/code&gt; isolation (as the sandbox significantly limits the potential impact on the host).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exploitability Constraints:&lt;/strong&gt;
This vulnerability is not a &amp;ldquo;remote code execution&amp;rdquo; flaw in the traditional sense. Exploitation requires:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Installing a malicious plugin.&lt;/li&gt;
&lt;li&gt;Having write access to the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt; of the root repository.&lt;/li&gt;
&lt;li&gt;Merging a malicious &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt; into the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Currently, creating plugins is undocumented, and the only known plugin using executors is &lt;code&gt;hop&lt;/code&gt;. Since any modification to &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt; requires a merge into the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt;, a standard code review process should easily detect and prevent such malicious entries.&lt;/p&gt;

&lt;h2 id=&quot;4-immediate-mitigations&quot;&gt;4. Immediate Mitigations&lt;/h2&gt;

&lt;p&gt;If you cannot wait for the 0.4.0 release, we recommend applying the following measures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Disable Executors:&lt;/strong&gt; Set your forge configuration for executors to &lt;code&gt;None&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secure Executors:&lt;/strong&gt; Use more secure executors, such as &lt;code&gt;bwrap&lt;/code&gt; or &lt;code&gt;container&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plugin Trust:&lt;/strong&gt; Only install and enable plugins from trusted sources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access Control:&lt;/strong&gt; Strictly monitor and review all changes to &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt;, specifically focusing on authorized system commands.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;5-patch-schedule&quot;&gt;5. Patch Schedule&lt;/h2&gt;

&lt;p&gt;This vulnerability will be fully addressed in &lt;strong&gt;version 0.4.0&lt;/strong&gt;.
We have decided not to release an intermediate 0.3.1 patch, as the attack vector relies on an &amp;ldquo;insider&amp;rdquo; or &amp;ldquo;compromised account&amp;rdquo; scenario that is effectively mitigated by standard code review practices.&lt;/p&gt;

&lt;h2 id=&quot;6-security-contact&quot;&gt;6. Security Contact&lt;/h2&gt;

&lt;p&gt;Transparency and security are core values at GitRoot. If you discover any other potential vulnerabilities or have questions regarding this report, please contact us directly at &lt;a href=&quot;blog/contact@gitroot.dev&quot;&gt;contact@gitroot.dev&lt;/a&gt;.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/security-vulnerability-1.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/modifying-API-SDK-plugin.md</link><title>Modifying the API in the SDK Plugin</title><pubDate>2026/04/06</pubDate><description>&lt;h1 id=&quot;modifying-the-api-in-the-sdk-plugin&quot;&gt;Modifying the API in the SDK Plugin&lt;/h1&gt;

&lt;p&gt;The last feature for version 0.4 is about adding &lt;code&gt;mv&lt;/code&gt; (move) and &lt;code&gt;cp&lt;/code&gt; (copy) functions to the SDK plugin. I&amp;rsquo;m taking advantage of this development to explain how the plumbing works under the hood.&lt;/p&gt;

&lt;p&gt;Right now, the SDK plugin is undocumented because there are a lot of breaking changes coming. Every time I develop a new plugin, I discover better ways to do things, and the SDK needs to adapt.&lt;/p&gt;

&lt;p&gt;For example, when I added &lt;code&gt;apex_code&lt;/code&gt; (a plugin that takes code as a string and returns syntax-highlighted HTML), I wondered: &amp;ldquo;Is this a standalone plugin?&amp;rdquo;. Not really, because there&amp;rsquo;s no file to manipulate, neither in input nor output. Instead, it manages blocks inside an existing file. The SDK wasn&amp;rsquo;t originally built with that use case in mind.&lt;/p&gt;

&lt;p&gt;Anyway, I think explaining how the SDK works through a concrete example will be helpful, if not for you, at least for me.&lt;/p&gt;

&lt;h2 id=&quot;what-is-the-sdk-s-job&quot;&gt;What is the SDK&amp;rsquo;s job?&lt;/h2&gt;

&lt;p&gt;The SDK exists to pass data from GitRoot (the host) to the plugin (the guest). As you know, GitRoot uses WebAssembly (WASM) to manage plugins. Unfortunately, WASM is still a bit low-level to implement: between the host and the guest, only integers can travel.&lt;/p&gt;

&lt;p&gt;Wait, only integers?&lt;/p&gt;

&lt;p&gt;Yes. So, how do we send a string? The host can store any kind of data in the guest&amp;rsquo;s memory. GitRoot stores a string, retrieves the pointer (the memory address) and the size, and sends those two integers to the guest. The guest then retrieves the string from its own memory. It&amp;rsquo;s a complex dance just for a simple string. The SDK is here to hide all that mess.&lt;/p&gt;

&lt;p&gt;When you build a plugin, you just implement an interface. The SDK handles the rest: you receive objects and strings, and you send back data without ever worrying about the underlying WASM pointers.&lt;/p&gt;

&lt;h2 id=&quot;what-does-a-concrete-implementation-look-like&quot;&gt;What does a concrete implementation look like?&lt;/h2&gt;

&lt;p&gt;When I add a new function, I always start with the host side. All functions are located in &lt;a href=&quot;app/server/plugin/runtime.go&quot;&gt;runtime.go&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We need to register the function in &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/wazero/wazero&quot;&gt;wazero&lt;/a&gt;, our WASM runtime. I add two versions because I haven&amp;rsquo;t found a clean way to bridge the difference between AssemblyScript and Go/Rust yet. Hopefully, I&amp;rsquo;ll figure that out one day.&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;r.wazRun.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#50fa7b&quot;&gt;NewFunctionBuilder&lt;/span&gt;().&lt;span style=&quot;color:#50fa7b&quot;&gt;WithFunc&lt;/span&gt;(r.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(&lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;)).&lt;span style=&quot;color:#50fa7b&quot;&gt;Export&lt;/span&gt;(&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;moveFile&amp;#34;&lt;/span&gt;).
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#50fa7b&quot;&gt;NewFunctionBuilder&lt;/span&gt;().&lt;span style=&quot;color:#50fa7b&quot;&gt;WithFunc&lt;/span&gt;(r.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(&lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;)).&lt;span style=&quot;color:#50fa7b&quot;&gt;Export&lt;/span&gt;(&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;moveFileAS&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I implement the builder:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(forAS &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;interface&lt;/span&gt;{} {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; !forAS {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, ptr, size, ptr2, size2 &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#6272a4&quot;&gt;// from ptr/size to string or object
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;			r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromPath, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	} &lt;span style=&quot;color:#ff79c6&quot;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, ptr, ptr2 &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#6272a4&quot;&gt;// from ptr to string or object
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;			r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromPath, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Yeah, I know. As a Gopher, seeing &lt;code&gt;interface{}&lt;/code&gt; as a return type and all these uint32 feels wrong. But hey, we&amp;rsquo;re not here for a code review, we&amp;rsquo;re here to add an API. If you have a cleaner way, feel free to submit a graft!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We want to allow any plugin to move a file. Let&amp;rsquo;s define the internal &lt;code&gt;moveFile&lt;/code&gt; function:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromPath, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#6272a4&quot;&gt;//need to do something...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Actually, there&amp;rsquo;s a catch, GitRoot uses three internal filesystems: one for the git worktree, one for web content, and one for the cache. Even if they are abstracted under one root, the code often expects a specific FS. I&amp;rsquo;ll probably clean this up later, but for now:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#6272a4&quot;&gt;//need to do something...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now your plugin can call &lt;code&gt;moveFile(&amp;quot;cache&amp;quot;, &amp;quot;artifacts/myBin&amp;quot;, &amp;quot;web&amp;quot;, &amp;quot;releases/myBin-v1.0&amp;quot;)&lt;/code&gt; to publish an artifact, or &lt;code&gt;moveFile(&amp;quot;web&amp;quot;, &amp;quot;index.html&amp;quot;, &amp;quot;web&amp;quot;, &amp;quot;old/index.html&amp;quot;)&lt;/code&gt; to back up a file.&lt;/p&gt;

&lt;p&gt;I won&amp;rsquo;t bore you with the implementation logic here. If you&amp;rsquo;re curious, check the &lt;a href=&quot;app/server/plugin/runtime.go&quot;&gt;code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is how the final builder looks, converting pointers to strings:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(forAS &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;interface&lt;/span&gt;{} {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; !forAS {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, fromFsPtr, fromFsSize, fromPathPtr, fromGlobPathSize, toFsPtr, toFsSize, toPathPtr, toPathSize &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;uint64&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile fromFs&amp;#34;&lt;/span&gt;, fromFsPtr, fromFsSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile fromGlobPath&amp;#34;&lt;/span&gt;, fromPathPtr, fromGlobPathSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile toFs&amp;#34;&lt;/span&gt;, toFsPtr, toFsSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile toPath&amp;#34;&lt;/span&gt;, toPathPtr, toPathSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	} &lt;span style=&quot;color:#ff79c6&quot;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, fromFsPtr, fromPathPtr, toFsPtr, toPathPtr &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;uint64&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS fromFs&amp;#34;&lt;/span&gt;, fromFsPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS fromGlobPath&amp;#34;&lt;/span&gt;, fromPathPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS toFs&amp;#34;&lt;/span&gt;, toFsPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS toPath&amp;#34;&lt;/span&gt;, toPathPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L38&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L38&quot;&gt;38&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L39&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L39&quot;&gt;39&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L40&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L40&quot;&gt;40&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L41&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L41&quot;&gt;41&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L42&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L42&quot;&gt;42&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L43&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L43&quot;&gt;43&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When I need to pass complex objects, I use &lt;code&gt;json.Marshal&lt;/code&gt; in GitRoot and &lt;code&gt;json.Unmarshal&lt;/code&gt; in the SDK. It works, but don&amp;rsquo;t get too attached to it, I might change the serialization method in the future.&lt;/p&gt;

&lt;h2 id=&quot;inside-the-sdk&quot;&gt;Inside the SDK&lt;/h2&gt;

&lt;p&gt;Now that the host is ready, we need to implement the other side: the SDK itself. I usually start with Go, then move to Rust and AssemblyScript. Sometimes I forget the others&amp;hellip; shame on me! I&amp;rsquo;m planning to automate this with some boilerplate generation later.&lt;/p&gt;

&lt;p&gt;In the &lt;a href=&quot;app/libs/golang/plugin/model/server.go&quot;&gt;server&lt;/a&gt; interface:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;type&lt;/span&gt; Server &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;interface&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#6272a4&quot;&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;    &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#6272a4&quot;&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make it more &amp;ldquo;user-friendly&amp;rdquo;, I use a struct to represent the filesystem in &lt;a href=&quot;app/libs/golang/plugin/model/fs.go&quot;&gt;model/fs.go&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (g &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;GrFs) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(fromPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; g.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(g.base, fromPath, g.base, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (g &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;GrFs) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileToFs&lt;/span&gt;(fromPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, toFs &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;GrFs, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; g.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(g.base, fromPath, toFs.base, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The actual WASM call happens in &lt;a href=&quot;app/libs/golang/plugin/server.go&quot;&gt;plugin/server.go&lt;/a&gt;, where we transform strings back into pointers:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;// MoveFile implements [model.ServerNeeded].
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (s &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;pServer) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(fromFs model.FsBase, fromPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, toFs model.FsBase, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr, size &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;string&lt;/span&gt;(fromFs))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr2, size2 &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(fromPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr3, size3 &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;string&lt;/span&gt;(toFs))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr4, size4 &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	resPtrSize &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;_moveFile&lt;/span&gt;(ptr, size, ptr2, size2, ptr3, size3, ptr4, size4)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(fromFs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(fromPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(toFs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;ptrSizeToError&lt;/span&gt;(resPtrSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally, the link to the host via &lt;code&gt;go:wasmimport&lt;/code&gt; in &lt;a href=&quot;app/libs/golang/plugin/imports.go&quot;&gt;plugin/imports.go&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;//go:wasmimport gitroot moveFile
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;_moveFile&lt;/span&gt;(fromFsPtr, fromFsSize, fromPathPtr, fromPathSize, toFsPtr, toFsSize, toPathPtr, toPathSize &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) (ptrSize &lt;span style=&quot;color:#8be9fd&quot;&gt;uint64&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;how-to-use-it&quot;&gt;How to use it?&lt;/h2&gt;

&lt;p&gt;Now, any plugin can use this. Here&amp;rsquo;s a simple Go plugin that triggers a build whenever a &lt;code&gt;.go&lt;/code&gt; file is modified and moves the binary to the web folder.&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;package&lt;/span&gt; main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;path/filepath&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;strings&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	gitroot &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;gitroot.dev/libs/golang/plugin&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;gitroot.dev/libs/golang/plugin/model&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;var&lt;/span&gt; defaultRun = []model.PluginRun{{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	Path:   &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*.go&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	When:   model.PluginRunWhenAll,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	Branch: []&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;{&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	Write: model.PluginWrite{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		Web: []model.PluginWriteRight{{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			Path: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;releases/main&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			Can:  model.PluginWriteRightCanAll,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;type&lt;/span&gt; Plugin &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	server model.Server
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    needToBuild &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;Init&lt;/span&gt;(repoName &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, confHasChanged &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;, serializedConf &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;StartCommit&lt;/span&gt;(commit model.Commit) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                  { &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;AddFile&lt;/span&gt;(file model.File) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L38&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L38&quot;&gt;38&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L39&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L39&quot;&gt;39&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L40&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L40&quot;&gt;40&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L41&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L41&quot;&gt;41&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;ModFile&lt;/span&gt;(file model.File) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L42&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L42&quot;&gt;42&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L43&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L43&quot;&gt;43&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L44&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L44&quot;&gt;44&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L45&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L45&quot;&gt;45&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L46&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L46&quot;&gt;46&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;DelFile&lt;/span&gt;(file model.File) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L47&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L47&quot;&gt;47&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L48&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L48&quot;&gt;48&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L49&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L49&quot;&gt;49&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L50&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L50&quot;&gt;50&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L51&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L51&quot;&gt;51&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;EndCommit&lt;/span&gt;(commit model.Commit) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                    { &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L52&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L52&quot;&gt;52&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L53&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L53&quot;&gt;53&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;Finish&lt;/span&gt;() &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L54&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L54&quot;&gt;54&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; p.needToBuild {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L55&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L55&quot;&gt;55&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        res, _ &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; p.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;Exec&lt;/span&gt;(model.Exec{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L56&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L56&quot;&gt;56&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            Cmds: []model.Cmd{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L57&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L57&quot;&gt;57&lt;/a&gt;&lt;/span&gt;&lt;span&gt;                {Cmd: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;go&amp;#34;&lt;/span&gt;, Args: []&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;{&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;build&amp;#34;&lt;/span&gt;}},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L58&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L58&quot;&gt;58&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L59&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L59&quot;&gt;59&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            Artifacts: []&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;{&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L60&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L60&quot;&gt;60&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L61&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L61&quot;&gt;61&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; p.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;Cache&lt;/span&gt;().&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileToFs&lt;/span&gt;(res.Artifacts[&lt;span style=&quot;color:#bd93f9&quot;&gt;0&lt;/span&gt;], p.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;Webcontent&lt;/span&gt;(), filepath.&lt;span style=&quot;color:#50fa7b&quot;&gt;Join&lt;/span&gt;(&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;releases&amp;#34;&lt;/span&gt;, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L62&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L62&quot;&gt;62&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L63&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L63&quot;&gt;63&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L64&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L64&quot;&gt;64&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L65&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L65&quot;&gt;65&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L66&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L66&quot;&gt;66&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;Build&lt;/span&gt;(server model.Server) model.Plugin {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L67&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L67&quot;&gt;67&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;&amp;amp;&lt;/span&gt;Plugin{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L68&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L68&quot;&gt;68&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		server: server,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L69&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L69&quot;&gt;69&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L70&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L70&quot;&gt;70&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L71&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L71&quot;&gt;71&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L72&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L72&quot;&gt;72&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;//go:wasmexport install
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L73&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L73&quot;&gt;73&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L74&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L74&quot;&gt;74&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	gitroot.&lt;span style=&quot;color:#50fa7b&quot;&gt;Register&lt;/span&gt;(defaultRun, Build)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L75&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L75&quot;&gt;75&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;is-that-all&quot;&gt;Is that all?&lt;/h2&gt;

&lt;p&gt;Nope. I still need to update the &lt;a href=&quot;app/libs/rust/plugin/src/fs.rs&quot;&gt;Rust&lt;/a&gt; and &lt;a href=&quot;app/libs/ts/plugin/fs.ts&quot;&gt;AssemblyScript&lt;/a&gt; SDKs and, of course, add &lt;a href=&quot;app/libs/golang/plugin/test/fakeserver.go&quot;&gt;tests&lt;/a&gt;. But this should give you a good idea of how the plumbing works.&lt;/p&gt;

&lt;p&gt;If anything is unclear, &lt;a href=&quot;contact.html&quot;&gt;let me know&lt;/a&gt;!&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/modifying-API-SDK-plugin.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/how-to-help_fr_11-2025.md</link><title>Comment contribuer au développement de GitRoot</title><pubDate>2025/11/18</pubDate><description>&lt;h1 id=&quot;comment-contribuer-au-développement-de-gitroot-11-2025&quot;&gt;Comment contribuer au développement de GitRoot 11-2025&lt;/h1&gt;

&lt;p&gt;Je viens tout juste de lire &lt;a target=&quot;_blank&quot; href=&quot;https://antonin.delpeuch.eu/posts/too-much-time-on-your-hands/&quot;&gt;cette article&lt;/a&gt; et je n&amp;rsquo;arrête pas d&amp;rsquo;y penser. Je le met en perspective des contributeurs de GitRoot. Je suis le seul contributeur (à part mon ami Damien qui a corrigé de nombreuses fautes, merci à lui) sur ce projet. Et comme le montre ce &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge/statuses/01K98BCFEB9V430JD9TFQ32YZN&quot;&gt;toot&lt;/a&gt; j&amp;rsquo;ai un travail pour payer les factures, 1 femme et 2 enfants qui m&amp;rsquo;occupent pratiquement tout le reste du temps. À ça je rajoute GitRoot, pour le moment je m&amp;rsquo;éclate et je tiens bon mais pour combien de temps je vais y arriver ?&lt;/p&gt;

&lt;p&gt;Alors pourquoi pas de contributeurs externes sur GitRoot ?&lt;/p&gt;

&lt;p&gt;Je me dit souvent que le projet est jeune, trop jeune, pour que ça soit accessible à un developpeur externe. Parfois je me dit qu&amp;rsquo;un meilleurs système de ticket serait nécessaire pour avoir un label &amp;ldquo;welcome new contribution&amp;rdquo;. Parfois je me dit qu&amp;rsquo;une fois que j&amp;rsquo;aurais déployé les plugins sdk et fait un market place de plugins, enfin vous pourrez venir m&amp;rsquo;aider à faire que GitRoot soit un vrai projet open-source.&lt;/p&gt;

&lt;p&gt;Mais, car il y a un mais, cette liste ne s&amp;rsquo;arrêtera jamais. Soyons franc il y aura toujours un truc de plus à faire avant que vous puissiez m&amp;rsquo;aider à faire que GitRoot soit au top. J&amp;rsquo;ai donc décidé de faire la chose la plus simple à l&amp;rsquo;heure actuelle pour vous expliquer comment m&amp;rsquo;aider: un article de blog.&lt;/p&gt;

&lt;p&gt;Ici, je vais vous expliquer tout ce qu&amp;rsquo;il est possible de faire en l&amp;rsquo;état actuel du projet. Pas ce qu&amp;rsquo;il sera possible de faire un jour &lt;em&gt;peu être&lt;/em&gt;, mais ce que vous pouvez faire là maintenant. Je réitèrerais ce genre d&amp;rsquo;article tant que nécessaire.&lt;/p&gt;

&lt;h2 id=&quot;pré-requis&quot;&gt;Pré-requis&lt;/h2&gt;

&lt;p&gt;Il n&amp;rsquo;y a pas grand chose à faire pour être prêt à aider sur GitRoot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;vous devez avoir &lt;a target=&quot;_blank&quot; href=&quot;https://git-scm.com/&quot;&gt;git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;git clone ssh://gitroot.dev&lt;/li&gt;
&lt;li&gt;cd gitroot.dev&lt;/li&gt;
&lt;li&gt;./gitroot/init.sh&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si vous souhaitez coder il vous faudra en plus:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/bash/manual/bash.html&quot;&gt;bash&lt;/a&gt; et &lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/make/&quot;&gt;make&lt;/a&gt; (si vous êtes dev il y a de grandes chances que vous les aillez déjà)&lt;/li&gt;
&lt;li&gt;installer &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt;, c&amp;rsquo;est le plus simple pour avoir tous les compilateurs dans les bonnes versions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Et voila vous êtes bon pour commencer à aider. Mais que faire ? Et bien comme tout bon developpeur je vais vous répondre &amp;ldquo;ça dépend&amp;rdquo;. Ça dépend de ce que vous voulez faire ! Je vais lister dessous un ensemble de taches possibles, vous choisissez.&lt;/p&gt;

&lt;h2 id=&quot;avant-de-commencer&quot;&gt;Avant de commencer&lt;/h2&gt;

&lt;p&gt;Avant de vous dire ce que vous pouvez faire, je me dit qu&amp;rsquo;il serait bien de vous expliquer ce que moi je fais. Donc pour cette version 0.3.0 ma priorité absolue est de faire en sorte que GitRoot build GitRoot lui même. Aujourd&amp;rsquo;hui à chaque release je fais les binaires sur ma macine et je les scp. Je veux qu&amp;rsquo;avec cette version 0.3.0 cela ne soit plus le cas.&lt;/p&gt;

&lt;p&gt;Donc, si j&amp;rsquo;arrive à me retenir, je ne devrais pas faire les tickets qui suivent. Vous avez tout le temps dont vous souhaitez pour les faire. Le plus simple est, je pense, de nous retrouver sur &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;matrix channel gitroot&lt;/a&gt; pour en discutter.&lt;/p&gt;

&lt;p&gt;On peut également en parler par mail &lt;a href=&quot;blog/contact@gitroot.dev&quot;&gt;contact@gitroot.dev&lt;/a&gt; ou sur le fediverse &lt;cite class=&quot;informative&quot;&gt;&lt;a href=&quot;#forge@gitrrot.dev&quot;&gt;&lt;sup&gt;[forge@gitrrot.dev]&lt;/sup&gt;&lt;/a&gt;&lt;/cite&gt;(&lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;https://gts.gitroot.dev/@forge&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;qu-est-ce-qu-on-peut-faire&quot;&gt;Qu&amp;rsquo;est-ce qu&amp;rsquo;on peut faire ?&lt;/h2&gt;

&lt;h3 id=&quot;corriger-les-fautes&quot;&gt;Corriger les fautes&lt;/h3&gt;

&lt;p&gt;L&amp;rsquo;anglais n&amp;rsquo;est pas ma langue d&amp;rsquo;origine et je pense qu&amp;rsquo;il y a beaucoup de fautes, donc une tache pourrais être de lire tous les md dans &lt;code&gt;/doc/&lt;/code&gt;, &lt;code&gt;/blog/&lt;/code&gt; et pourquoi pas &lt;code&gt;/issues/&lt;/code&gt;. Vous corrigez, faites une branche et poussez. Je m&amp;rsquo;occupe du reste !&lt;/p&gt;

&lt;h3 id=&quot;moi-je-veux-faire-du-web&quot;&gt;Moi je veux faire du web&lt;/h3&gt;

&lt;p&gt;Ce qui serait top, ce serait de créer (ou de trouver) un thème. Aujourd&amp;rsquo;hui GitRoot utilise &lt;a target=&quot;_blank&quot; href=&quot;https://simplecss.org/&quot;&gt;simplecss&lt;/a&gt;, et il n&amp;rsquo;a besoin que de &lt;code&gt;css classless&lt;/code&gt;, il en existe beaucoup mais vous pouvez aussi créer celui de GitRoot.&lt;/p&gt;

&lt;p&gt;Le plus simple pour essayer est de lancer la commande &lt;code&gt;make testsuite&lt;/code&gt; quand elle a fini rendez vous dans &lt;code&gt;cd /tmp/repo1&lt;/code&gt; éditez le fichier &lt;code&gt;style.css&lt;/code&gt; puis &lt;code&gt;git add .&lt;/code&gt; &lt;code&gt;git commit -m &amp;quot;try style&amp;quot;&lt;/code&gt; et &lt;code&gt;git push&lt;/code&gt;, ouvrez &lt;a target=&quot;_blank&quot; href=&quot;http://127.0.0.1:4546/repo1&quot;&gt;http://127.0.0.1:4546/repo1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Quand vous êtes content faites une branche avec votre css dans &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/resources/styles/&quot;&gt;apex&lt;/a&gt;, puis &lt;code&gt;git add .&lt;/code&gt; et &lt;code&gt;git push&lt;/code&gt; je m&amp;rsquo;occupe du reste.&lt;/p&gt;

&lt;h3 id=&quot;moi-je-veux-coder&quot;&gt;Moi je veux coder&lt;/h3&gt;

&lt;p&gt;Bien, pas de soucis, voici une liste de petites tâches (enfin elles me paraissent petites mais si c&amp;rsquo;est pas le cas pour vous dites le moi):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;en golang:

&lt;ul&gt;
&lt;li&gt;dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/grafter/&quot;&gt;grafter&lt;/a&gt; pouvoir afficher &lt;a href=&quot;issues/3d33-grafter-dco.html&quot;&gt;autre chose&lt;/a&gt; dans le 1er message d&amp;rsquo;un graft. La bannière est en dure dans le plugin, il faudrait l&amp;rsquo;exporter dans la conf du plugin et l&amp;rsquo;utiliser.&lt;/li&gt;
&lt;li&gt;dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/silo/&quot;&gt;silo&lt;/a&gt; permettre de &lt;a href=&quot;issues/76ec-silo-more-selects.html&quot;&gt;grouper par fichier ou date&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;plus difficile mais j&amp;rsquo;aimerais bien avoir plus de &lt;a href=&quot;issues/e6d8-apex-alert-support.html&quot;&gt;renderer&lt;/a&gt; dans &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/&quot;&gt;apex&lt;/a&gt;, par exemple les infos, warning et autre&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;en typescript:

&lt;ul&gt;
&lt;li&gt;dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; il y a un bug sur le rss &lt;a href=&quot;issues/3986-pollen-rss-grow-infinite.html&quot;&gt;il est jamais nettoyé&lt;/a&gt;. Rajouté une option (ou pas, 100 en dur me semble être déjà pas mal) pour limiter le nombre d&amp;rsquo;&lt;code&gt;&amp;lt;item&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;toujours dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; pourquoi pas rajouter un &lt;a target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/JSON_Feed&quot;&gt;json feed&lt;/a&gt; en plus du rss. On peut serializer du json en assemblescript.&lt;/li&gt;
&lt;li&gt;plus difficile mais il serait bien de pouvoir dire au plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; de générer un rss avec le contenu des fichiers. Ça permettrait à GitRoot d&amp;rsquo;avoir un rss affichant les articles du blog par exemple. Aujourd&amp;rsquo;hui il ne met dans le rss que les commits.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;en rust:

&lt;ul&gt;
&lt;li&gt;faire ce que Ygor à dit dans &lt;a target=&quot;_blank&quot; href=&quot;https://floss.social/@ygor/115474535138702961&quot;&gt;https://floss.social/@ygor/115474535138702961&lt;/a&gt; je ne suis pas un developpeur rust et transformer des &lt;code&gt;String&lt;/code&gt; en &lt;code&gt;&amp;amp;str&lt;/code&gt; me parle pas beaucoup mais si vous l&amp;rsquo;êtes je suis sûr que c&amp;rsquo;est une petite tâche. Le code se trouve dans le &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; et le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/hop/&quot;&gt;hop&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;ajouter un &lt;a href=&quot;issues/9f00-rust-sdk-fake-server-test.html&quot;&gt;fakeServer&lt;/a&gt; au &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; pour faciliter les tests d&amp;rsquo;un plugin, voici celui de go &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&quot;&gt;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;moi-je-suis-sysadmin&quot;&gt;Moi je suis sysadmin&lt;/h3&gt;

&lt;p&gt;Parfait il y a de quoi faire aussi:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;aujourd&amp;rsquo;hui il y a trop d&amp;rsquo;étapes pour releaser GitRoot, &lt;a href=&quot;issues/dba0-simplify-release.html&quot;&gt;automatiser&lt;/a&gt; au maximum m&amp;rsquo;aiderais beaucoup.&lt;/li&gt;
&lt;li&gt;si vous avez un petit de place sur un de vos serveurs, &lt;a href=&quot;doc/tutorials/deploy_instance.html&quot;&gt;tester de déployer une instance&lt;/a&gt; juste pour le fun. Peu être vous pourrez améliorer la doc ou trouver quelque chose qui ne marche plus. Ou ajouter un paragraphe pour utiliser gitroot avec nginx ou autre.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;moi-je-veux-tester&quot;&gt;Moi je veux tester&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;tous les tests sont en bash dans &lt;a href=&quot;app/testsuite/runtest.sh&quot;&gt;testsuite/runtest.sh&lt;/a&gt; se trouve le plus gros test, à côté il y en d&amp;rsquo;autres

&lt;ul&gt;
&lt;li&gt;exécutez les pour voir s&amp;rsquo;ils marchent chez vous me parait une bonne première tâche&lt;/li&gt;
&lt;li&gt;en créer de nouveaux&lt;/li&gt;
&lt;li&gt;améliorer les existants en sortant par exemple des fonctions helper comme &lt;code&gt;quiet_git()&lt;/code&gt;, &lt;code&gt;report()&lt;/code&gt; ou &lt;code&gt;mySleep()&lt;/code&gt; dans d&amp;rsquo;autres fichiers (et les importer) serait d&amp;rsquo;une grande aide pour le projet. Vous pouvez aussi créer des nouveaux pour &lt;code&gt;killGitroot()&lt;/code&gt; qui est fait en début de chaque fichier. Ou &lt;code&gt;createProject()&lt;/code&gt; qui consiste à chaque fois à aller dans le root repository et ajouter son nom dans le fichier &lt;code&gt;.gitroot/repositories.yml&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;moi-je-veux-juste-réfléchir-discutter&quot;&gt;Moi je veux juste réfléchir/discutter&lt;/h3&gt;

&lt;p&gt;Tu es le bienvenue sur le &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;channel gitroot&lt;/a&gt; dans matrix. Ou par mail &lt;code&gt;contact [AT] gitroot.dev&lt;/code&gt; ou sur le fediverse &lt;code&gt;@forge@gitroot.dev&lt;/code&gt; &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;sur ce serveur&lt;/a&gt;.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/how-to-help_fr_11-2025.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/how-to-help_en_11-2025.md</link><title>How to help building GitRoot</title><pubDate>2025/11/18</pubDate><description>&lt;h1 id=&quot;how-to-help-building-gitroot-11-2025&quot;&gt;How to help building GitRoot 11-2025&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;This article is the translation of &lt;a href=&quot;blog/how-to-help_fr_11-2025.html&quot;&gt;french version&lt;/a&gt; thanks to &lt;a target=&quot;_blank&quot; href=&quot;https://www.deepl.com&quot;&gt;DeepL&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I just read &lt;a target=&quot;_blank&quot; href=&quot;https://antonin.delpeuch.eu/posts/too-much-time-on-your-hands/&quot;&gt;this article&lt;/a&gt; and I can&amp;rsquo;t stop thinking about it. I&amp;rsquo;m putting it into perspective for GitRoot contributors. I&amp;rsquo;m the only contributor (apart from my friend Damien, who corrected numerous typos, thank you to him) on this project. And as this &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge/statuses/01K98BCFEB9V430JD9TFQ32YZN&quot;&gt;toot&lt;/a&gt; shows, I have a job to pay the bills, a wife and two children who take up practically all the rest of my time. On top of that, I have GitRoot. For now, I&amp;rsquo;m having a blast and hanging in there, but how long can I keep it up?&lt;/p&gt;

&lt;p&gt;So why not have external contributors on GitRoot?&lt;/p&gt;

&lt;p&gt;I often tell myself that the project is young, too young, to be accessible to an external developer. Sometimes I think that a better issues system would be necessary to have a “welcome new contribution” label. Sometimes I think that once I&amp;rsquo;ve deployed the SDK plugins and created a plugin marketplace, then you can come and help me make GitRoot a true open-source project.&lt;/p&gt;

&lt;p&gt;But, because there is a but, this list will never end. Let&amp;rsquo;s be honest, there will always be something else to do before you can help me make GitRoot the best it can be. So I decided to do the simplest thing possible right now to explain how you can help me: a blog post.&lt;/p&gt;

&lt;p&gt;Here, I&amp;rsquo;ll explain everything that can be done in the current state of the project. Not what might be possible someday, but what you can do right now. I&amp;rsquo;ll repeat this kind of article as often as necessary.&lt;/p&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;There isn&amp;rsquo;t much you need to do to get ready to help out on GitRoot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you need to have &lt;a target=&quot;_blank&quot; href=&quot;https://git-scm.com/&quot;&gt;git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;git clone ssh://gitroot.dev&lt;/li&gt;
&lt;li&gt;cd gitroot.dev&lt;/li&gt;
&lt;li&gt;./gitroot/init.sh&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to code, you will also need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/bash/manual/bash.html&quot;&gt;bash&lt;/a&gt; and &lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/make/&quot;&gt;make&lt;/a&gt; (if you are a developer, chances are you already have them)&lt;/li&gt;
&lt;li&gt;Install &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt;, which is the easiest way to get all the right versions of the compilers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And now you&amp;rsquo;re ready to start helping. But what should you do? Well, like any good developer, I&amp;rsquo;ll answer, &amp;ldquo;It depends.&amp;rdquo;. It depends on what you want to do! I&amp;rsquo;ll list a set of possible tasks below, and you can choose.&lt;/p&gt;

&lt;h2 id=&quot;before-we-begin&quot;&gt;Before we begin&lt;/h2&gt;

&lt;p&gt;Before telling you what you can do, I thought it would be a good idea to explain what I am doing. So, for this version 0.3.0, my top priority is to make sure that GitRoot builds GitRoot itself. Currently, for each release, I build the binaries on my machine and then scp them. I want this to no longer be the case with version 0.3.0.&lt;/p&gt;

&lt;p&gt;So, if I can restrain myself, I shouldn&amp;rsquo;t have to do the following tickets. You have all the time you need to do them. The easiest thing, I think, is to meet up on &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;matrix channel gitroot&lt;/a&gt; to discuss them.&lt;/p&gt;

&lt;p&gt;We can also talk about it by email &lt;a href=&quot;blog/contact@gitroot.dev&quot;&gt;contact@gitroot.dev&lt;/a&gt; or on the fediverse &lt;cite class=&quot;informative&quot;&gt;&lt;a href=&quot;#forge@gitrrot.dev&quot;&gt;&lt;sup&gt;[forge@gitrrot.dev]&lt;/sup&gt;&lt;/a&gt;&lt;/cite&gt;(&lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;https://gts.gitroot.dev/@forge&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;what-can-we-do&quot;&gt;What can we do?&lt;/h2&gt;

&lt;h3 id=&quot;correct-mistakes&quot;&gt;Correct mistakes&lt;/h3&gt;

&lt;p&gt;English is not my native language, and I think there are a lot of typos, so one task could be to read all the MD files in &lt;code&gt;/doc/&lt;/code&gt;, &lt;code&gt;/blog/&lt;/code&gt;, and maybe even &lt;code&gt;/issues/&lt;/code&gt;. Correct them, create a branch, and push. I&amp;rsquo;ll take care of the rest!&lt;/p&gt;

&lt;h3 id=&quot;i-want-to-work-on-the-web&quot;&gt;I want to work on the web&lt;/h3&gt;

&lt;p&gt;It would be great to create (or find) a theme. GitRoot currently uses &lt;a target=&quot;_blank&quot; href=&quot;https://simplecss.org/&quot;&gt;simplecss&lt;/a&gt;, and it only needs &lt;code&gt;classless CSS&lt;/code&gt;. There are many available, but you can also create one for GitRoot.&lt;/p&gt;

&lt;p&gt;The easiest way to try it out is to run the &lt;code&gt;make testsuite&lt;/code&gt; command. When it&amp;rsquo;s finished, go to &lt;code&gt;cd /tmp/repo1&lt;/code&gt;, edit the &lt;code&gt;style.css&lt;/code&gt; file, then &lt;code&gt;git add .&lt;/code&gt; &lt;code&gt;git commit -m &amp;quot;try style&amp;quot;&lt;/code&gt; and &lt;code&gt;git push&lt;/code&gt;. Open &lt;a target=&quot;_blank&quot; href=&quot;http://127.0.0.1:4546/repo1&quot;&gt;http://127.0.0.1:4546/repo1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When you are satisfied, create a branch with your CSS in &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/resources/styles/&quot;&gt;apex&lt;/a&gt;, then &lt;code&gt;git add .&lt;/code&gt; and &lt;code&gt;git push&lt;/code&gt;. I&amp;rsquo;ll take care of the rest.&lt;/p&gt;

&lt;h3 id=&quot;i-want-to-code&quot;&gt;I want to code&lt;/h3&gt;

&lt;p&gt;Okay, no worries, here&amp;rsquo;s a list of small tasks (well, they seem small to me, but if that&amp;rsquo;s not the case for you, let me know):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;in golang:

&lt;ul&gt;
&lt;li&gt;in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/grafter/&quot;&gt;grafter&lt;/a&gt; plugin, be able to display &lt;a href=&quot;issues/3d33-grafter-dco.html&quot;&gt;something else&lt;/a&gt; in the first message of a graft. The banner is hard-coded in the plugin; it would need to be exported to the plugin configuration and used.&lt;/li&gt;
&lt;li&gt;In the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/silo/&quot;&gt;silo&lt;/a&gt; plugin, allow &lt;a href=&quot;issues/76ec-silo-more-selects.html&quot;&gt;grouping by file or date&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;More difficult, but I would like to have more &lt;a href=&quot;issues/e6d8-apex-alert-support.html&quot;&gt;renderers&lt;/a&gt; in &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/&quot;&gt;apex&lt;/a&gt;, for example info, warnings, and others&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;in typescript:

&lt;ul&gt;
&lt;li&gt;in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; plugin there is a bug in the RSS feed &lt;a href=&quot;issues/3986-pollen-rss-grow-infinite.html&quot;&gt;it is never cleaned up&lt;/a&gt;. Added an option (or not, 100 seems fine to me) to limit the number of &lt;code&gt;&amp;lt;item&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Still in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; plugin, why not add a &lt;a target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/JSON_Feed&quot;&gt;json feed&lt;/a&gt; in addition to the RSS feed? We can serialize json in assemblescript.&lt;/li&gt;
&lt;li&gt;More difficult, but it would be nice to be able to tell the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; plugin to generate an RSS feed with the contents of the files. This would allow GitRoot to have an RSS feed displaying blog articles, for example. Currently, it only puts commits in the RSS feed.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;in Rust:

&lt;ul&gt;
&lt;li&gt;Do what Ygor said in &lt;a target=&quot;_blank&quot; href=&quot;https://floss.social/@ygor/115474535138702961&quot;&gt;https://floss.social/@ygor/115474535138702961&lt;/a&gt; I&amp;rsquo;m not a Rust developer and converting &lt;code&gt;String&lt;/code&gt; to &lt;code&gt;&amp;amp;str&lt;/code&gt; doesn&amp;rsquo;t mean much to me, but if you are, I&amp;rsquo;m sure it&amp;rsquo;s a small task. The code can be found in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; and the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/hop/&quot;&gt;hop&lt;/a&gt; plugin.&lt;/li&gt;
&lt;li&gt;Add a &lt;a href=&quot; issues/9f00-rust-sdk-fake-server-test.html&quot;&gt;fakeServer&lt;/a&gt; to the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; to facilitate plugin testing. Here is the one for Go &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&quot;&gt;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;i-m-a-sysadmin&quot;&gt;I&amp;rsquo;m a sysadmin.&lt;/h3&gt;

&lt;p&gt;Perfect, there&amp;rsquo;s plenty to do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Today, there are too many steps to release GitRoot. &lt;a href=&quot;issues/dba0-simplify-release.html&quot;&gt;Automating&lt;/a&gt; as much as possible would help me a lot.&lt;/li&gt;
&lt;li&gt;If you have a little space on one of your servers, &lt;a href=&quot;doc/tutorials/deploy_instance.html&quot;&gt;try deploying an instance&lt;/a&gt; just for fun. Maybe you can improve the documentation or find something that no longer works. Or add a paragraph on using gitroot with nginx or something else.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;i-want-to-test&quot;&gt;I want to test&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;All tests are in bash in &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/testsuite/&quot;&gt;/app/testsuite/&lt;/a&gt;. The largest test is located in &lt;code&gt;runtest.sh&lt;/code&gt;, along with others.

&lt;ul&gt;
&lt;li&gt;Running them to see if they work on your system seems like a good first task to me.&lt;/li&gt;
&lt;li&gt;Creating new ones&lt;/li&gt;
&lt;li&gt;Improving existing ones by, for example, moving helper functions such as &lt;code&gt;quiet_git()&lt;/code&gt;, &lt;code&gt;report()&lt;/code&gt;, or &lt;code&gt;mySleep()&lt;/code&gt; to other files (and importing them) would be a great help to the project. You can also create new ones for &lt;code&gt;killGitroot()&lt;/code&gt;, which is done at the beginning of each file. Or &lt;code&gt;createProject()&lt;/code&gt;, which consists of going to the root repository each time and adding its name to the &lt;code&gt;.gitroot/repositories.yml&lt;/code&gt; file&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;i-just-want-to-think-discuss&quot;&gt;I just want to think/discuss&lt;/h3&gt;

&lt;p&gt;You are welcome to join the &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;gitroot channel&lt;/a&gt; on Matrix. Or contact us by email at &lt;code&gt;contact [AT] gitroot.dev&lt;/code&gt; or on the Fediverse at &lt;code&gt;@forge@gitroot.dev&lt;/code&gt; &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;on this server&lt;/a&gt;.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/how-to-help_en_11-2025.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/issues.md</link><title>Issues, Tickets, Discussions... Call them what you want!</title><pubDate>2026/06/08</pubDate><description>&lt;h1 id=&quot;issues-tickets-discussions-call-them-what-you-want&quot;&gt;Issues, Tickets, Discussions&amp;hellip; Call them what you want!&lt;/h1&gt;

&lt;p&gt;Many of you, when checking out &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev&quot;&gt;https://gitroot.dev&lt;/a&gt;, have told me that the &lt;code&gt;🐞 Issues&lt;/code&gt; button shouldn&amp;rsquo;t be labeled &amp;ldquo;Issues&amp;rdquo;, but rather &amp;ldquo;Tickets&amp;rdquo; or &amp;ldquo;Discussions&amp;rdquo;.&lt;/p&gt;

&lt;p&gt;First of all, thanks for the feedback! I really appreciate it, that&amp;rsquo;s how I can build a better forge. Of course, I will change the label, but developing a forge means having a lot of different areas to work on: issues/boards, git, code, CI/CD, and with GitRoot, even more, like plugins and WASM&amp;hellip;&lt;/p&gt;

&lt;p&gt;So, why don&amp;rsquo;t I just change this label right away? In reality, this label isn&amp;rsquo;t hardcoded inside GitRoot. It&amp;rsquo;s only there because I configured plugins to display it. I want to take this opportunity to explain how GitRoot works, because the platform is developed around this exact concept: how to customize a forge at the project level.&lt;/p&gt;

&lt;h2 id=&quot;the-original-idea&quot;&gt;The Original Idea&lt;/h2&gt;

&lt;p&gt;Imagine you are developing a forge, and to avoid vendor lock-in, you decide not to put anything outside of Git. You choose not to use any database at all. In this context, how do you design an &amp;ldquo;issue&amp;rdquo; system?&lt;/p&gt;

&lt;p&gt;In traditional forges, an &amp;ldquo;issue&amp;rdquo; consists of a title and a description created by a user. We can represent it like this:&lt;/p&gt;
&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;379.68002&quot; height=&quot;332.2&quot; viewBox=&quot;0 0 379.68002 332.2&quot;&gt;&lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;379.68002&quot; height=&quot;332.2&quot; fill=&quot;transparent&quot;/&gt;&lt;defs&gt;&lt;marker id=&quot;arrow-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 10 5 L 0 10 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-start-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;4.5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 5 L 10 10 L 10 0 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-open-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;1&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1 7 L 18 13 V 1 Z&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-open-start-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;18&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1 7 L 18 13 V 1 Z&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-dep-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;13&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 18 7 L 9 13 L 14 7 L 9 1 Z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-dep-start-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;6&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 5 7 L 9 13 L 1 7 L 9 1 Z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;path id=&quot;edge-0&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-0&quot; d=&quot;M 135.960,121.250 L 96.540,175.250&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot; x=&quot;85.37&quot; y=&quot;134.25&quot; width=&quot;61.76&quot; height=&quot;28.00&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.85&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.35&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;116.25&quot; y=&quot;151.75&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;116.25&quot; dy=&quot;0.00&quot;&gt;Author&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-1&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-1&quot; d=&quot;M 231.960,121.250 L 255.380,175.250&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-1&quot; data-label-kind=&quot;center&quot; x=&quot;217.27&quot; y=&quot;134.25&quot; width=&quot;52.80&quot; height=&quot;28.00&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.85&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.35&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-1&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;243.67&quot; y=&quot;151.75&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;243.67&quot; dy=&quot;0.00&quot;&gt;State&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;rect x=&quot;87.90&quot; y=&quot;8.00&quot; width=&quot;192.12&quot; height=&quot;113.25&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;line x1=&quot;93.90&quot; y1=&quot;46.95&quot; x2=&quot;274.02&quot; y2=&quot;46.95&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1.0&quot;/&gt;&lt;text x=&quot;183.96&quot; y=&quot;34.00&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;183.96&quot; dy=&quot;0.00&quot; font-weight=&quot;600&quot;&gt;Issue&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;117.90&quot; y=&quot;69.70&quot; text-anchor=&quot;start&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;117.90&quot; dy=&quot;0.00&quot;&gt;int id&lt;/tspan&gt;&lt;tspan x=&quot;117.90&quot; dy=&quot;17.85&quot;&gt;string title&lt;/tspan&gt;&lt;tspan x=&quot;117.90&quot; dy=&quot;17.85&quot;&gt;string description&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;203.08&quot; y=&quot;175.25&quot; width=&quot;168.60&quot; height=&quot;148.95&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;line x1=&quot;209.08&quot; y1=&quot;214.20&quot; x2=&quot;365.68&quot; y2=&quot;214.20&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1.0&quot;/&gt;&lt;text x=&quot;287.38&quot; y=&quot;201.25&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;287.38&quot; dy=&quot;0.00&quot; font-weight=&quot;600&quot;&gt;Status&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;233.08&quot; y=&quot;236.95&quot; text-anchor=&quot;start&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;0.00&quot;&gt;&amp;lt;&amp;lt;enumeration&amp;gt;&amp;gt;&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;Open&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;TODO&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;DOING&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;CLOSE&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;8.00&quot; y=&quot;175.25&quot; width=&quot;145.08&quot; height=&quot;95.40&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;line x1=&quot;14.00&quot; y1=&quot;214.20&quot; x2=&quot;147.08&quot; y2=&quot;214.20&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1.0&quot;/&gt;&lt;text x=&quot;80.54&quot; y=&quot;201.25&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;80.54&quot; dy=&quot;0.00&quot; font-weight=&quot;600&quot;&gt;User&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;38.00&quot; y=&quot;236.95&quot; text-anchor=&quot;start&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;38.00&quot; dy=&quot;0.00&quot;&gt;int id&lt;/tspan&gt;&lt;tspan x=&quot;38.00&quot; dy=&quot;17.85&quot;&gt;string email&lt;/tspan&gt;&lt;/text&gt;&lt;/svg&gt;
&lt;p&gt;Easy, right? Well, how do you represent that in GitRoot using only files? This question was running through my mind for months before I finally found a solution.&lt;/p&gt;

&lt;h2 id=&quot;the-basic-solution&quot;&gt;The Basic Solution&lt;/h2&gt;

&lt;p&gt;Since I only have files at my disposal, what actually constitutes an &amp;ldquo;issue&amp;rdquo;? I need to add some metadata to a file so the system understands that this file is an issue and not, say, a &lt;code&gt;main.go&lt;/code&gt;. I’ll probably want to group all these &amp;ldquo;issues&amp;rdquo; into a specific directory, because while GitRoot technically lets you do whatever you want (even dropping an issue right in the middle of your source code), I personally prefer to keep my files organized!&lt;/p&gt;

&lt;p&gt;A simple text format that handles metadata perfectly is Markdown. I use Markdown here, but any other markup language could work. Let&amp;rsquo;s create a file named &lt;code&gt;issues/1.md&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;id: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;user: email@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;status: OPEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# My title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt;9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;My description where I can detail what my problem is.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yes, it really is that simple. In GitRoot, to create an issue, you just create a file, add some metadata, commit, and push. Boom, you have an issue.&lt;/p&gt;

&lt;p&gt;Now, you might be thinking: &amp;ldquo;Well, I lose a lot of control with that, the status could be anything!&amp;rdquo; or &amp;ldquo;How are other users supposed to know what to put where?&amp;rdquo;. You&amp;rsquo;re right, and that is exactly where plugins enter the game.&lt;/p&gt;

&lt;h2 id=&quot;a-better-solution&quot;&gt;A Better Solution&lt;/h2&gt;

&lt;p&gt;If I simplify this as much as possible, what does an external contributor actually need to provide to create an issue? Just a title and a description. So, let&amp;rsquo;s create a file named &lt;code&gt;issues/my_issue.md&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# My title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;My description where I can detail what my problem is.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we run &lt;code&gt;git commit &amp;amp;&amp;amp; git push&lt;/code&gt;. Since I have configured the &lt;a href=&quot;plugins/name/ladybug.html&quot;&gt;ladybug plugin&lt;/a&gt; like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: ladybug
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: issues/**/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;func&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: issues/**/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;callfunc&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;default&lt;/span&gt;: autogenerated
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;mandatory&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: id
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;type&lt;/span&gt;: crc16
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;default&lt;/span&gt;: OPEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;mandatory&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: status
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;type&lt;/span&gt;: string
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;default&lt;/span&gt;: email@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;mandatory&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: user
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;type&lt;/span&gt;: string
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can read this configuration as: &amp;ldquo;For every Markdown file in the &lt;code&gt;issues&lt;/code&gt; directory across all branches, if the file doesn&amp;rsquo;t have these metadata fields, automatically add these default values.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, if you are a Git purist, a server creating commits right on top of yours might sound a bit radical, or even terrifying. But look closely at the &lt;code&gt;write&lt;/code&gt; block in the YAML configuration above:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: issues/**/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;GitRoot doesn&amp;rsquo;t just let plugins run wild, the project configuration must explicitly grant the plugin permission to write or modify files on specific paths. If you don&amp;rsquo;t declare these rights, GitRoot will block the plugin from making any changes.&lt;/p&gt;

&lt;p&gt;When granted, GitRoot intercepts your push, lets the plugin inject the missing metadata, and safely generates a follow-up commit. And don&amp;rsquo;t worry about your history: if you decide to &lt;code&gt;git rebase -i&lt;/code&gt; later to squash or fixup your commits, plugins won&amp;rsquo;t generate duplicate commits if no metadata has changed.&lt;/p&gt;

&lt;p&gt;What about security or external users trying to forge metadata? Since contributors cannot push directly to the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt; (like &lt;code&gt;main&lt;/code&gt;), they have to go through a branch and need to be merged. This means every single change to an issue&amp;rsquo;s status or metadata is subjected to a code review.&lt;/p&gt;

&lt;p&gt;If an external user tries to close someone else&amp;rsquo;s issue or manipulate IDs, it will stand out like a sore thumb in the git diff. Maintainers retain absolute control and can simply reject the rogue contribution. Security doesn&amp;rsquo;t rely on complex database permission tables, but on the battle-tested git review workflow you already know and trust.&lt;/p&gt;

&lt;p&gt;So, once you push, the server-side plugin does its job, and the next time you run &lt;code&gt;git pull&lt;/code&gt;, your local file will automatically look like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;id: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;user: email@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;status: OPEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# My title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt;9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;My description where I can detail what my problem is.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bingo! You get the exact same file, with the exact same metadata you wanted. Of course, a lot more needs to be added, like defining a &lt;code&gt;type: user&lt;/code&gt; to assign an issue, or perhaps a &lt;code&gt;type: status&lt;/code&gt; to define an enum of allowed values. But I think you see where I&amp;rsquo;m going with this feature!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: If you&amp;rsquo;re wondering about the barrier to entry for non-technical contributors, my long-term vision is to allow plugins to intercept standard HTTP requests. Tomorrow, a classic web form will be able to POST to GitRoot and automatically commit a &lt;code&gt;.md&lt;/code&gt; file under the hood. But for now, we are proudly dev-centric!)&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;so-what-about-the-label&quot;&gt;So, What About the Label?&lt;/h2&gt;

&lt;p&gt;Throughout all of this, you’ll notice that the term &amp;ldquo;issue&amp;rdquo; only appears within the plugins themselves. Nowhere have we talked about that &lt;code&gt;🐞 Issues&lt;/code&gt; button yet. We actually need to introduce another plugin before we can tackle that topic.&lt;/p&gt;

&lt;p&gt;First, we need a file that lists all of these &amp;ldquo;issues&amp;rdquo;. That’s exactly why the &lt;a href=&quot;plugins/name/silo.html&quot;&gt;silo plugin&lt;/a&gt; exists. It inspects all files and groups them whenever it finds a matching pattern.&lt;/p&gt;

&lt;p&gt;To list all open &amp;ldquo;issues&amp;rdquo;, we configure it like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: silo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;func&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: boards/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - append
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;callfunc&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;boards&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;description&lt;/span&gt;: All open issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;for&lt;/span&gt;: issues/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;format&lt;/span&gt;: table
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;paginator&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;50&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;selects&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;status: (.*)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;user: (.*)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;sort&lt;/span&gt;: select[0]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;sortOrder&lt;/span&gt;: desc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;tableHeader&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;| | status | user |&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;title&lt;/span&gt;: All issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;to&lt;/span&gt;: boards/issues.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;where&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;status: OPEN&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can read this configuration as: &amp;ldquo;For every file in the main branch, create a board called &amp;lsquo;All issues&amp;rsquo; containing links to all files matching &lt;code&gt;status: OPEN&lt;/code&gt;. Display them as a table with &lt;code&gt;status&lt;/code&gt; and &lt;code&gt;user&lt;/code&gt; columns.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After running &lt;code&gt;git commit &amp;amp;&amp;amp; git push &amp;amp;&amp;amp; git pull&lt;/code&gt;, you will get a new file at &lt;code&gt;boards/issues.md&lt;/code&gt; containing something like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# All issues
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;All open issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;|                                   | status | user            |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;| --------------------------------- | ------ | --------------- |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;| [&lt;span style=&quot;color:#ff79c6&quot;&gt;My title&lt;/span&gt;](&lt;span style=&quot;color:#50fa7b&quot;&gt;../issues/my_issue.md&lt;/span&gt;) | OPEN   | email@email.com |
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Silo has aggregated all your open issues into a single dashboard. Of course, you could easily create another board for closed issues in a separate file like &lt;code&gt;boards/closed.md&lt;/code&gt;, it&amp;rsquo;s entirely up to you.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: If you are sweating at the thought of a plugin scanning your entire repository on every single push, don&amp;rsquo;t panic. &lt;code&gt;silo&lt;/code&gt;, thanks to the plugin-sdk API, doesn&amp;rsquo;t do a full disk scan. It only processes the incremental diffs generated by the incoming commit. This is precisely why we restrict its scope to the &lt;code&gt;main&lt;/code&gt; branch in this example.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now that we have a page representing all our issues, we can finally render it in the browser. Because, as you might have guessed, the label causing all this debate lives in the Web UI.&lt;/p&gt;

&lt;h2 id=&quot;the-web-is-where-it-happens&quot;&gt;The Web is Where It Happens&lt;/h2&gt;

&lt;p&gt;To render my &lt;code&gt;boards/issues.md&lt;/code&gt; file on the web, I use the &lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex&lt;/a&gt; plugin, in conjunction with the &lt;a href=&quot;plugins/name/apex_markdown.html&quot;&gt;apex_markdown&lt;/a&gt; plugin, of course.&lt;/p&gt;

&lt;p&gt;These plugins take files from git and render them directly in the Web UI. I configure them like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: apex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;func&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - append
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - append
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;callfunc&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;pluginname&lt;/span&gt;: apex_markdown
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;funcname&lt;/span&gt;: renderMd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;menu&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 🏠 Home
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 📖 Documentation
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L38&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L38&quot;&gt;38&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /doc/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L39&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L39&quot;&gt;39&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 🔖 Versions
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L40&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L40&quot;&gt;40&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /CHANGELOG.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L41&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L41&quot;&gt;41&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: �� Plugins
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L42&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L42&quot;&gt;42&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /plugins/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L43&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L43&quot;&gt;43&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: �� Issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L44&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L44&quot;&gt;44&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /boards/issues.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L45&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L45&quot;&gt;45&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 🚀 Code
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L46&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L46&quot;&gt;46&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /worktree/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L47&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L47&quot;&gt;47&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: ☕ Blog
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L48&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L48&quot;&gt;48&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /blog/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L49&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L49&quot;&gt;49&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: �� Contact
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L50&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L50&quot;&gt;50&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /contact.html
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oh, do you see it now? Yes, right there: &lt;code&gt;display: 🐞 Issues&lt;/code&gt;. This is the exact configuration that tells Apex to add a global menu item with the &lt;code&gt;🐞 Issues&lt;/code&gt; label, pointing to the &lt;code&gt;/boards/issues.html&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;If you are wondering how Apex magically knows where to find your &amp;ldquo;issues&amp;rdquo;, the secret is: it doesn&amp;rsquo;t.&lt;/p&gt;

&lt;p&gt;Apex isn&amp;rsquo;t a smart router, it&amp;rsquo;s just a file translator. It blindly takes the Markdown structure generated by GitRoot and renders it into HTML. Because I know Apex transforms &lt;code&gt;boards/issues.md&lt;/code&gt; into &lt;code&gt;boards/issues.html&lt;/code&gt;, I simply hardcode that target URL in my menu configuration (&lt;code&gt;link: /boards/issues.html&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;And what about the links inside the table? Apex has a clever built-in rewrite engine. When it parses your generated board file, it automatically rewrites every internal Markdown link, turning &lt;code&gt;[My title](../issues/my_issue.md)&lt;/code&gt; into a standard web-friendly &lt;code&gt;&amp;lt;a href=&amp;quot;../issues/my_issue.html&amp;quot;&amp;gt;&lt;/code&gt;. No database, no dynamic routing, just plain old static files mapped one-to-one.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;If you change the label to &lt;code&gt;🐞 Tickets&lt;/code&gt; or &lt;code&gt;💡 Discussions&lt;/code&gt;, the &amp;ldquo;issue to rename issues into something else&amp;rdquo; can finally be closed. Ideally, you might also want to rename some directories in the other plugins&amp;rsquo; configurations, but it&amp;rsquo;s really not a big deal.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;m pretty sure you now understand why I didn&amp;rsquo;t immediately jump on resolving this task: everything we just saw is pure configuration, completely separated from the GitRoot core code.&lt;/p&gt;

&lt;p&gt;And to be perfectly honest, I haven&amp;rsquo;t changed it yet because none of the traditional options really appeal to me. I don&amp;rsquo;t particularly like &amp;ldquo;Tickets&amp;rdquo; or &amp;ldquo;Discussions&amp;rdquo;, maybe it should be &amp;ldquo;Feature Requests&amp;rdquo; instead (and yes, you can now model that in GitRoot too!).&lt;/p&gt;

&lt;p&gt;In fact, I personally call them &lt;code&gt;TODO&lt;/code&gt;s, because in reality, they are all just waiting for me to get them done.&lt;/p&gt;

&lt;h3 id=&quot;wait-why-stop-at-issues&quot;&gt;Wait&amp;hellip; Why stop at issues?&lt;/h3&gt;

&lt;p&gt;This is where the true power of GitRoot will blow your mind.&lt;/p&gt;

&lt;p&gt;Because GitRoot doesn&amp;rsquo;t care about databases, and because everything is just a file inside Git, the &lt;code&gt;silo&lt;/code&gt; plugin can aggregate anything. I don&amp;rsquo;t need a separate &lt;code&gt;TODO&lt;/code&gt; page for my tickets, because I already have one that automatically tracks the &lt;code&gt;//TODO&lt;/code&gt; comments inside my actual source code.&lt;/p&gt;

&lt;p&gt;Yes, you read that right. You can visit it live at &lt;a href=&quot;boards/todos.html&quot;&gt;https://gitroot.dev/boards/todos.html&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Every time I write a &lt;code&gt;//TODO&lt;/code&gt; in a &lt;code&gt;.go&lt;/code&gt;, &lt;code&gt;.rs&lt;/code&gt; or &lt;code&gt;.ts&lt;/code&gt; file, GitRoot parses it, aggregates it, and generates a dynamic dashboard. Tickets, documentation, source code comments&amp;hellip; it&amp;rsquo;s all the exact same data structure.&lt;/p&gt;

&lt;p&gt;To be clear, it doesn’t magically transform your source code comments into full-blown database-like issues with IDs and statuses. It simply extracts them to give you a centralized, living view of your technical debt, right alongside your official tickets. It’s the exact same data aggregation pipeline, tailored for two different use cases.&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to discover more plugins and configurations, feel free to browse through the &lt;a href=&quot;plugins&quot;&gt;🧩 Plugins section&lt;/a&gt;. However, for now, the best way to explore remains checking out the actual &lt;a href=&quot;.gitroot/plugins.yml&quot;&gt;GitRoot plugins.yml configuration file&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
</description><guid>https://gitroot.dev//blog/issues.md</guid></item>
<item><author>silo@gitroot.dev (silo)</author><link>https://gitroot.dev//blog/issues.md</link><title>Issues, Tickets, Discussions... Call them what you want!</title><pubDate>2026/06/08</pubDate><description>&lt;h1 id=&quot;issues-tickets-discussions-call-them-what-you-want&quot;&gt;Issues, Tickets, Discussions&amp;hellip; Call them what you want!&lt;/h1&gt;

&lt;p&gt;Many of you, when checking out &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev&quot;&gt;https://gitroot.dev&lt;/a&gt;, have told me that the &lt;code&gt;🐞 Issues&lt;/code&gt; button shouldn&amp;rsquo;t be labeled &amp;ldquo;Issues&amp;rdquo;, but rather &amp;ldquo;Tickets&amp;rdquo; or &amp;ldquo;Discussions&amp;rdquo;.&lt;/p&gt;

&lt;p&gt;First of all, thanks for the feedback! I really appreciate it, that&amp;rsquo;s how I can build a better forge. Of course, I will change the label, but developing a forge means having a lot of different areas to work on: issues/boards, git, code, CI/CD, and with GitRoot, even more, like plugins and WASM&amp;hellip;&lt;/p&gt;

&lt;p&gt;So, why don&amp;rsquo;t I just change this label right away? In reality, this label isn&amp;rsquo;t hardcoded inside GitRoot. It&amp;rsquo;s only there because I configured plugins to display it. I want to take this opportunity to explain how GitRoot works, because the platform is developed around this exact concept: how to customize a forge at the project level.&lt;/p&gt;

&lt;h2 id=&quot;the-original-idea&quot;&gt;The Original Idea&lt;/h2&gt;

&lt;p&gt;Imagine you are developing a forge, and to avoid vendor lock-in, you decide not to put anything outside of Git. You choose not to use any database at all. In this context, how do you design an &amp;ldquo;issue&amp;rdquo; system?&lt;/p&gt;

&lt;p&gt;In traditional forges, an &amp;ldquo;issue&amp;rdquo; consists of a title and a description created by a user. We can represent it like this:&lt;/p&gt;
&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;379.68002&quot; height=&quot;332.2&quot; viewBox=&quot;0 0 379.68002 332.2&quot;&gt;&lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;379.68002&quot; height=&quot;332.2&quot; fill=&quot;transparent&quot;/&gt;&lt;defs&gt;&lt;marker id=&quot;arrow-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 10 5 L 0 10 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-start-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;4.5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 5 L 10 10 L 10 0 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-open-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;1&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1 7 L 18 13 V 1 Z&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-open-start-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;18&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1 7 L 18 13 V 1 Z&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-dep-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;13&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 18 7 L 9 13 L 14 7 L 9 1 Z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-dep-start-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;6&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 5 7 L 9 13 L 1 7 L 9 1 Z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;path id=&quot;edge-0&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-0&quot; d=&quot;M 135.960,121.250 L 96.540,175.250&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot; x=&quot;85.37&quot; y=&quot;134.25&quot; width=&quot;61.76&quot; height=&quot;28.00&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.85&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.35&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;116.25&quot; y=&quot;151.75&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;116.25&quot; dy=&quot;0.00&quot;&gt;Author&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-1&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-1&quot; d=&quot;M 231.960,121.250 L 255.380,175.250&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-1&quot; data-label-kind=&quot;center&quot; x=&quot;217.27&quot; y=&quot;134.25&quot; width=&quot;52.80&quot; height=&quot;28.00&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.85&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.35&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-1&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;243.67&quot; y=&quot;151.75&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;243.67&quot; dy=&quot;0.00&quot;&gt;State&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;rect x=&quot;87.90&quot; y=&quot;8.00&quot; width=&quot;192.12&quot; height=&quot;113.25&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;line x1=&quot;93.90&quot; y1=&quot;46.95&quot; x2=&quot;274.02&quot; y2=&quot;46.95&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1.0&quot;/&gt;&lt;text x=&quot;183.96&quot; y=&quot;34.00&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;183.96&quot; dy=&quot;0.00&quot; font-weight=&quot;600&quot;&gt;Issue&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;117.90&quot; y=&quot;69.70&quot; text-anchor=&quot;start&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;117.90&quot; dy=&quot;0.00&quot;&gt;int id&lt;/tspan&gt;&lt;tspan x=&quot;117.90&quot; dy=&quot;17.85&quot;&gt;string title&lt;/tspan&gt;&lt;tspan x=&quot;117.90&quot; dy=&quot;17.85&quot;&gt;string description&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;203.08&quot; y=&quot;175.25&quot; width=&quot;168.60&quot; height=&quot;148.95&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;line x1=&quot;209.08&quot; y1=&quot;214.20&quot; x2=&quot;365.68&quot; y2=&quot;214.20&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1.0&quot;/&gt;&lt;text x=&quot;287.38&quot; y=&quot;201.25&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;287.38&quot; dy=&quot;0.00&quot; font-weight=&quot;600&quot;&gt;Status&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;233.08&quot; y=&quot;236.95&quot; text-anchor=&quot;start&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;0.00&quot;&gt;&amp;lt;&amp;lt;enumeration&amp;gt;&amp;gt;&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;Open&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;TODO&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;DOING&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;CLOSE&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;8.00&quot; y=&quot;175.25&quot; width=&quot;145.08&quot; height=&quot;95.40&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;line x1=&quot;14.00&quot; y1=&quot;214.20&quot; x2=&quot;147.08&quot; y2=&quot;214.20&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1.0&quot;/&gt;&lt;text x=&quot;80.54&quot; y=&quot;201.25&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;80.54&quot; dy=&quot;0.00&quot; font-weight=&quot;600&quot;&gt;User&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;38.00&quot; y=&quot;236.95&quot; text-anchor=&quot;start&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;38.00&quot; dy=&quot;0.00&quot;&gt;int id&lt;/tspan&gt;&lt;tspan x=&quot;38.00&quot; dy=&quot;17.85&quot;&gt;string email&lt;/tspan&gt;&lt;/text&gt;&lt;/svg&gt;
&lt;p&gt;Easy, right? Well, how do you represent that in GitRoot using only files? This question was running through my mind for months before I finally found a solution.&lt;/p&gt;

&lt;h2 id=&quot;the-basic-solution&quot;&gt;The Basic Solution&lt;/h2&gt;

&lt;p&gt;Since I only have files at my disposal, what actually constitutes an &amp;ldquo;issue&amp;rdquo;? I need to add some metadata to a file so the system understands that this file is an issue and not, say, a &lt;code&gt;main.go&lt;/code&gt;. I’ll probably want to group all these &amp;ldquo;issues&amp;rdquo; into a specific directory, because while GitRoot technically lets you do whatever you want (even dropping an issue right in the middle of your source code), I personally prefer to keep my files organized!&lt;/p&gt;

&lt;p&gt;A simple text format that handles metadata perfectly is Markdown. I use Markdown here, but any other markup language could work. Let&amp;rsquo;s create a file named &lt;code&gt;issues/1.md&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;id: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;user: email@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;status: OPEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# My title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt;9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;My description where I can detail what my problem is.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yes, it really is that simple. In GitRoot, to create an issue, you just create a file, add some metadata, commit, and push. Boom, you have an issue.&lt;/p&gt;

&lt;p&gt;Now, you might be thinking: &amp;ldquo;Well, I lose a lot of control with that, the status could be anything!&amp;rdquo; or &amp;ldquo;How are other users supposed to know what to put where?&amp;rdquo;. You&amp;rsquo;re right, and that is exactly where plugins enter the game.&lt;/p&gt;

&lt;h2 id=&quot;a-better-solution&quot;&gt;A Better Solution&lt;/h2&gt;

&lt;p&gt;If I simplify this as much as possible, what does an external contributor actually need to provide to create an issue? Just a title and a description. So, let&amp;rsquo;s create a file named &lt;code&gt;issues/my_issue.md&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# My title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;My description where I can detail what my problem is.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we run &lt;code&gt;git commit &amp;amp;&amp;amp; git push&lt;/code&gt;. Since I have configured the &lt;a href=&quot;plugins/name/ladybug.html&quot;&gt;ladybug plugin&lt;/a&gt; like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: ladybug
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: issues/**/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;func&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: issues/**/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;callfunc&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;default&lt;/span&gt;: autogenerated
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;mandatory&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: id
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;type&lt;/span&gt;: crc16
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;default&lt;/span&gt;: OPEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;mandatory&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: status
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;type&lt;/span&gt;: string
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;default&lt;/span&gt;: email@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;mandatory&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: user
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;type&lt;/span&gt;: string
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can read this configuration as: &amp;ldquo;For every Markdown file in the &lt;code&gt;issues&lt;/code&gt; directory across all branches, if the file doesn&amp;rsquo;t have these metadata fields, automatically add these default values.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, if you are a Git purist, a server creating commits right on top of yours might sound a bit radical, or even terrifying. But look closely at the &lt;code&gt;write&lt;/code&gt; block in the YAML configuration above:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: issues/**/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;GitRoot doesn&amp;rsquo;t just let plugins run wild, the project configuration must explicitly grant the plugin permission to write or modify files on specific paths. If you don&amp;rsquo;t declare these rights, GitRoot will block the plugin from making any changes.&lt;/p&gt;

&lt;p&gt;When granted, GitRoot intercepts your push, lets the plugin inject the missing metadata, and safely generates a follow-up commit. And don&amp;rsquo;t worry about your history: if you decide to &lt;code&gt;git rebase -i&lt;/code&gt; later to squash or fixup your commits, plugins won&amp;rsquo;t generate duplicate commits if no metadata has changed.&lt;/p&gt;

&lt;p&gt;What about security or external users trying to forge metadata? Since contributors cannot push directly to the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt; (like &lt;code&gt;main&lt;/code&gt;), they have to go through a branch and need to be merged. This means every single change to an issue&amp;rsquo;s status or metadata is subjected to a code review.&lt;/p&gt;

&lt;p&gt;If an external user tries to close someone else&amp;rsquo;s issue or manipulate IDs, it will stand out like a sore thumb in the git diff. Maintainers retain absolute control and can simply reject the rogue contribution. Security doesn&amp;rsquo;t rely on complex database permission tables, but on the battle-tested git review workflow you already know and trust.&lt;/p&gt;

&lt;p&gt;So, once you push, the server-side plugin does its job, and the next time you run &lt;code&gt;git pull&lt;/code&gt;, your local file will automatically look like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;id: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;user: email@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;status: OPEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# My title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt;9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;My description where I can detail what my problem is.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bingo! You get the exact same file, with the exact same metadata you wanted. Of course, a lot more needs to be added, like defining a &lt;code&gt;type: user&lt;/code&gt; to assign an issue, or perhaps a &lt;code&gt;type: status&lt;/code&gt; to define an enum of allowed values. But I think you see where I&amp;rsquo;m going with this feature!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: If you&amp;rsquo;re wondering about the barrier to entry for non-technical contributors, my long-term vision is to allow plugins to intercept standard HTTP requests. Tomorrow, a classic web form will be able to POST to GitRoot and automatically commit a &lt;code&gt;.md&lt;/code&gt; file under the hood. But for now, we are proudly dev-centric!)&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;so-what-about-the-label&quot;&gt;So, What About the Label?&lt;/h2&gt;

&lt;p&gt;Throughout all of this, you’ll notice that the term &amp;ldquo;issue&amp;rdquo; only appears within the plugins themselves. Nowhere have we talked about that &lt;code&gt;🐞 Issues&lt;/code&gt; button yet. We actually need to introduce another plugin before we can tackle that topic.&lt;/p&gt;

&lt;p&gt;First, we need a file that lists all of these &amp;ldquo;issues&amp;rdquo;. That’s exactly why the &lt;a href=&quot;plugins/name/silo.html&quot;&gt;silo plugin&lt;/a&gt; exists. It inspects all files and groups them whenever it finds a matching pattern.&lt;/p&gt;

&lt;p&gt;To list all open &amp;ldquo;issues&amp;rdquo;, we configure it like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: silo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;func&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: boards/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - append
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;callfunc&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;boards&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;description&lt;/span&gt;: All open issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;for&lt;/span&gt;: issues/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;format&lt;/span&gt;: table
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;paginator&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;50&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;selects&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;status: (.*)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;user: (.*)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;sort&lt;/span&gt;: select[0]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;sortOrder&lt;/span&gt;: desc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;tableHeader&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;| | status | user |&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;title&lt;/span&gt;: All issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;to&lt;/span&gt;: boards/issues.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;where&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;status: OPEN&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can read this configuration as: &amp;ldquo;For every file in the main branch, create a board called &amp;lsquo;All issues&amp;rsquo; containing links to all files matching &lt;code&gt;status: OPEN&lt;/code&gt;. Display them as a table with &lt;code&gt;status&lt;/code&gt; and &lt;code&gt;user&lt;/code&gt; columns.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After running &lt;code&gt;git commit &amp;amp;&amp;amp; git push &amp;amp;&amp;amp; git pull&lt;/code&gt;, you will get a new file at &lt;code&gt;boards/issues.md&lt;/code&gt; containing something like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# All issues
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;All open issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;|                                   | status | user            |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;| --------------------------------- | ------ | --------------- |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;| [&lt;span style=&quot;color:#ff79c6&quot;&gt;My title&lt;/span&gt;](&lt;span style=&quot;color:#50fa7b&quot;&gt;../issues/my_issue.md&lt;/span&gt;) | OPEN   | email@email.com |
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Silo has aggregated all your open issues into a single dashboard. Of course, you could easily create another board for closed issues in a separate file like &lt;code&gt;boards/closed.md&lt;/code&gt;, it&amp;rsquo;s entirely up to you.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: If you are sweating at the thought of a plugin scanning your entire repository on every single push, don&amp;rsquo;t panic. &lt;code&gt;silo&lt;/code&gt;, thanks to the plugin-sdk API, doesn&amp;rsquo;t do a full disk scan. It only processes the incremental diffs generated by the incoming commit. This is precisely why we restrict its scope to the &lt;code&gt;main&lt;/code&gt; branch in this example.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now that we have a page representing all our issues, we can finally render it in the browser. Because, as you might have guessed, the label causing all this debate lives in the Web UI.&lt;/p&gt;

&lt;h2 id=&quot;the-web-is-where-it-happens&quot;&gt;The Web is Where It Happens&lt;/h2&gt;

&lt;p&gt;To render my &lt;code&gt;boards/issues.md&lt;/code&gt; file on the web, I use the &lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex&lt;/a&gt; plugin, in conjunction with the &lt;a href=&quot;plugins/name/apex_markdown.html&quot;&gt;apex_markdown&lt;/a&gt; plugin, of course.&lt;/p&gt;

&lt;p&gt;These plugins take files from git and render them directly in the Web UI. I configure them like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: apex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;func&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - append
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - append
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;callfunc&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;pluginname&lt;/span&gt;: apex_markdown
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;funcname&lt;/span&gt;: renderMd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;menu&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 🏠 Home
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 📖 Documentation
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L38&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L38&quot;&gt;38&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /doc/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L39&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L39&quot;&gt;39&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 🔖 Versions
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L40&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L40&quot;&gt;40&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /CHANGELOG.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L41&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L41&quot;&gt;41&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: �� Plugins
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L42&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L42&quot;&gt;42&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /plugins/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L43&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L43&quot;&gt;43&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: �� Issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L44&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L44&quot;&gt;44&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /boards/issues.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L45&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L45&quot;&gt;45&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 🚀 Code
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L46&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L46&quot;&gt;46&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /worktree/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L47&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L47&quot;&gt;47&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: ☕ Blog
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L48&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L48&quot;&gt;48&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /blog/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L49&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L49&quot;&gt;49&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: �� Contact
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L50&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L50&quot;&gt;50&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /contact.html
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oh, do you see it now? Yes, right there: &lt;code&gt;display: 🐞 Issues&lt;/code&gt;. This is the exact configuration that tells Apex to add a global menu item with the &lt;code&gt;🐞 Issues&lt;/code&gt; label, pointing to the &lt;code&gt;/boards/issues.html&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;If you are wondering how Apex magically knows where to find your &amp;ldquo;issues&amp;rdquo;, the secret is: it doesn&amp;rsquo;t.&lt;/p&gt;

&lt;p&gt;Apex isn&amp;rsquo;t a smart router, it&amp;rsquo;s just a file translator. It blindly takes the Markdown structure generated by GitRoot and renders it into HTML. Because I know Apex transforms &lt;code&gt;boards/issues.md&lt;/code&gt; into &lt;code&gt;boards/issues.html&lt;/code&gt;, I simply hardcode that target URL in my menu configuration (&lt;code&gt;link: /boards/issues.html&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;And what about the links inside the table? Apex has a clever built-in rewrite engine. When it parses your generated board file, it automatically rewrites every internal Markdown link, turning &lt;code&gt;[My title](../issues/my_issue.md)&lt;/code&gt; into a standard web-friendly &lt;code&gt;&amp;lt;a href=&amp;quot;../issues/my_issue.html&amp;quot;&amp;gt;&lt;/code&gt;. No database, no dynamic routing, just plain old static files mapped one-to-one.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;If you change the label to &lt;code&gt;🐞 Tickets&lt;/code&gt; or &lt;code&gt;💡 Discussions&lt;/code&gt;, the &amp;ldquo;issue to rename issues into something else&amp;rdquo; can finally be closed. Ideally, you might also want to rename some directories in the other plugins&amp;rsquo; configurations, but it&amp;rsquo;s really not a big deal.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;m pretty sure you now understand why I didn&amp;rsquo;t immediately jump on resolving this task: everything we just saw is pure configuration, completely separated from the GitRoot core code.&lt;/p&gt;

&lt;p&gt;And to be perfectly honest, I haven&amp;rsquo;t changed it yet because none of the traditional options really appeal to me. I don&amp;rsquo;t particularly like &amp;ldquo;Tickets&amp;rdquo; or &amp;ldquo;Discussions&amp;rdquo;, maybe it should be &amp;ldquo;Feature Requests&amp;rdquo; instead (and yes, you can now model that in GitRoot too!).&lt;/p&gt;

&lt;p&gt;In fact, I personally call them &lt;code&gt;TODO&lt;/code&gt;s, because in reality, they are all just waiting for me to get them done.&lt;/p&gt;

&lt;h3 id=&quot;wait-why-stop-at-issues&quot;&gt;Wait&amp;hellip; Why stop at issues?&lt;/h3&gt;

&lt;p&gt;This is where the true power of GitRoot will blow your mind.&lt;/p&gt;

&lt;p&gt;Because GitRoot doesn&amp;rsquo;t care about databases, and because everything is just a file inside Git, the &lt;code&gt;silo&lt;/code&gt; plugin can aggregate anything. I don&amp;rsquo;t need a separate &lt;code&gt;TODO&lt;/code&gt; page for my tickets, because I already have one that automatically tracks the &lt;code&gt;//TODO&lt;/code&gt; comments inside my actual source code.&lt;/p&gt;

&lt;p&gt;Yes, you read that right. You can visit it live at &lt;a href=&quot;boards/todos.html&quot;&gt;https://gitroot.dev/boards/todos.html&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Every time I write a &lt;code&gt;//TODO&lt;/code&gt; in a &lt;code&gt;.go&lt;/code&gt;, &lt;code&gt;.rs&lt;/code&gt; or &lt;code&gt;.ts&lt;/code&gt; file, GitRoot parses it, aggregates it, and generates a dynamic dashboard. Tickets, documentation, source code comments&amp;hellip; it&amp;rsquo;s all the exact same data structure.&lt;/p&gt;

&lt;p&gt;To be clear, it doesn’t magically transform your source code comments into full-blown database-like issues with IDs and statuses. It simply extracts them to give you a centralized, living view of your technical debt, right alongside your official tickets. It’s the exact same data aggregation pipeline, tailored for two different use cases.&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to discover more plugins and configurations, feel free to browse through the &lt;a href=&quot;plugins&quot;&gt;🧩 Plugins section&lt;/a&gt;. However, for now, the best way to explore remains checking out the actual &lt;a href=&quot;.gitroot/plugins.yml&quot;&gt;GitRoot plugins.yml configuration file&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
</description><guid>https://gitroot.dev//blog/issues.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.4.0.md</link><title>0.4.0</title><pubDate>2026/04/22</pubDate><description>&lt;h1 id=&quot;the-teenager-starts-building-its-own-future&quot;&gt;The teenager starts building its own future&lt;/h1&gt;

&lt;h2 id=&quot;if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes&lt;/h2&gt;

&lt;p&gt;A lot has been done in this new release of GitRoot. When I saw other FOSS projects claiming &amp;ldquo;this is the biggest release&amp;rdquo;, I thought it was a joke. But in fact, it&amp;rsquo;s true: every new release gets bigger and bigger. It&amp;rsquo;s just astonishing to start a sprint with almost zero ideas and, 4 months later, see all of this accomplished.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you skim this article, don&amp;rsquo;t forget to check out the conclusion about &lt;a href=&quot;blog/version-0.4.0.html#what-next&quot;&gt;&amp;ldquo;what&amp;rsquo;s next?&amp;rdquo;&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;the-thing-i-m-most-proud-of&quot;&gt;The thing i&amp;rsquo;m most proud of&lt;/h3&gt;

&lt;p&gt;Executors can finally build GitRoot! As I love to say: GitRoot can build GitRoot on GitRoot. And it was a pain to do. &lt;a href=&quot;blog/version-0.4.0.html#executor&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-thing-with-the-most-visibility&quot;&gt;The thing with the most visibility&lt;/h3&gt;

&lt;p&gt;The plugin registry is here! After a lot of reading and reflecting, I started this &amp;ldquo;epic story&amp;rdquo;. Today is only the beginning, but you can now find plugins directly at &lt;a href=&quot;plugins/index.html&quot;&gt;gitroot.dev/plugins&lt;/a&gt;. What&amp;rsquo;s next? &lt;a href=&quot;blog/version-0.4.0.html#plugin-registry&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-thing-that-changes-everything-but-was-so-simple&quot;&gt;The thing that changes everything but was so simple&lt;/h3&gt;

&lt;p&gt;Previously, a branch (and graft) couldn&amp;rsquo;t be rendered on the web. The problem was: if I have an &lt;code&gt;index.md&lt;/code&gt; in my git that renders at &lt;code&gt;myinstance.com/index.html&lt;/code&gt;, anyone can create a branch. If a &amp;ldquo;bad user&amp;rdquo; comes along, changes everything in this file, creates a branch, and pushes for review, where should this file render? Certainly not at &lt;code&gt;myinstance.com/index.html&lt;/code&gt;. &lt;a href=&quot;blog/version-0.4.0.html#apex-render-branch&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-thing-that-was-more-complex-than-i-thought&quot;&gt;The thing that was more complex than I thought&lt;/h3&gt;

&lt;p&gt;Apex was a big plugin. Basically, it manages the rendering of your git files to the web. But over time, it started to grow out of control because of markdown, code, worktrees, emojis&amp;hellip; A simple (in theory) solution was to split it into multiple plugins. &lt;a href=&quot;blog/version-0.4.0.html#plugin-deps&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-things-i-m-just-happy-are-done&quot;&gt;The things I&amp;rsquo;m just happy are done&lt;/h3&gt;

&lt;p&gt;No more mandatory signed commits! Well, it&amp;rsquo;s still better if you sign them, but this change means the &lt;code&gt;.gitroot/init.sh&lt;/code&gt; has been &lt;del&gt;deleted&lt;/del&gt; moved to a plugin. &lt;a href=&quot;blog/version-0.4.0.html#sigma-plugin&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Moving a file from git to web was hard, deleting a file was &amp;ldquo;not implemented&amp;rdquo;, and a lot of APIs were missing in the plugin-sdk. I got my hands dirty with all that, and now everything is running smoothly. &lt;a href=&quot;blog/version-0.4.0.html#fs-plugin-sdk&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;in-depth-details&quot;&gt;In-depth Details&lt;/h2&gt;

&lt;h3 id=&quot;executor&quot;&gt;Executor&lt;/h3&gt;

&lt;p&gt;When I started this feature, I just wanted a lightweight container to run &lt;code&gt;make build&lt;/code&gt; and output the result as a &lt;code&gt;gitroot-next&lt;/code&gt; canary binary. If I had known how much time and energy it would actually cost, I’m not sure I would have taken this road.&lt;/p&gt;

&lt;p&gt;At first, I chose &lt;a href=&quot;doc/technicals/executors.html#bwrap-executor&quot;&gt;bubblewrap&lt;/a&gt; as a lightweight container, and immediately started building a &lt;a href=&quot;doc/technicals/executors.html#container-executor&quot;&gt;container&lt;/a&gt; version. Then I realized I needed a &lt;a href=&quot;doc/technicals/executors.html#baremetal-executor&quot;&gt;bareMetal&lt;/a&gt; one. And then, I needed a way to run it on other machines, so &lt;a href=&quot;doc/technicals/executors.html#ssh-executor&quot;&gt;ssh&lt;/a&gt; came along.&lt;/p&gt;

&lt;p&gt;But when I launched it for the first time, I discovered that having &lt;code&gt;make&lt;/code&gt; isn&amp;rsquo;t enough for GitRoot, I need Golang, Rust, TinyGo, and so on. &amp;ldquo;No problem,&amp;rdquo; I thought, &amp;ldquo;I&amp;rsquo;ll use &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt; to manage that.&amp;rdquo; But running &lt;code&gt;mise install&lt;/code&gt; takes a lot of time (like 10-15 minutes on my computer). So, I built a cache system to ensure that hit only happens the first time.&lt;/p&gt;

&lt;p&gt;Still not the end! After that, I needed to configure port numbers, so I built a complete environment variable system, and used it to configure some &lt;code&gt;mise&lt;/code&gt; paths.&lt;/p&gt;

&lt;p&gt;Is it finished? Not yet! To be able to move a binary generated from a bwrap environment to the web, it has to travel a lot. And by &amp;ldquo;a lot,&amp;rdquo; I mean moving it to a place where the plugin that launched the executor can access it: its own cache. Then the plugin needs to move it from there to the web. No problem, I just added a bunch of &lt;a href=&quot;blog/modifying-API-SDK-plugin.html&quot;&gt;SDK API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After all that, some commands finally started working&amp;hellip; well, kind of. When I ran a bwrap or a container command, I couldn&amp;rsquo;t get the stats of what was happening inside. Having process stats is something I’m proud of (even if it’s still evolving in future versions), but seeing that bwrap reported 20 MB of RAM while the sub-process was actually consuming 80 MB was very frustrating. Furthermore, I needed a way to extract stats from the remote SSH executor.&lt;/p&gt;

&lt;p&gt;I tried a lot of things, but ultimately, I chose to build an &lt;a href=&quot;app/executor/main.go&quot;&gt;executor CLI&lt;/a&gt; to handle it. I now inject that binary inside your bwrap, container, or SSH environment. This little CLI launches commands, gathers system stats, command status, and logs. It packs everything into a well-formatted JSON and returns it to the executor in GitRoot. GitRoot then manages artifacts/caches and hands the result back to the calling plugin (probably &lt;a href=&quot;plugins/name/hop.html&quot;&gt;hop&lt;/a&gt;), which tells you what’s happening. In this case, it reports back so the &lt;a href=&quot;plugins/name/grafter.html&quot;&gt;grafter plugin&lt;/a&gt; can include the info in the current graft.&lt;/p&gt;
&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;295.9538&quot; height=&quot;429.0792&quot; viewBox=&quot;0 0 295.9538 429.0792&quot;&gt;&lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;295.9538&quot; height=&quot;429.0792&quot; fill=&quot;transparent&quot;/&gt;&lt;defs&gt;&lt;marker id=&quot;arrow-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 10 5 L 0 10 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-start-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;4.5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 5 L 10 10 L 10 0 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;path id=&quot;edge-0&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-0&quot; d=&quot;M 210.589,59.000 L 210.589,107.236&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot; x=&quot;105.97&quot; y=&quot;62.00&quot; width=&quot;90.62&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;151.28&quot; y=&quot;79.70&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;151.28&quot; dy=&quot;0.00&quot;&gt;Exec cmds&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-1&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-1&quot; d=&quot;M 210.679,158.236 L 210.679,206.472&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;path id=&quot;edge-2&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-2&quot; d=&quot;M 210.835,257.472 L 210.835,305.708&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-2&quot; data-label-kind=&quot;center&quot; x=&quot;17.60&quot; y=&quot;260.47&quot; width=&quot;179.24&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-2&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;107.22&quot; y=&quot;278.17&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;107.22&quot; dy=&quot;0.00&quot;&gt;bwrap/container/ssh&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-3&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-3&quot; d=&quot;M 210.955,356.708 L 210.955,370.079&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;path id=&quot;edge-4&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-4&quot; d=&quot;M 166.834,395.579 L 153.002,395.579 L 120.042,347.208 L 133.874,347.208&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;path id=&quot;edge-5&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-5&quot; d=&quot;M 133.874,315.208 L 8.000,315.208 L 8.000,247.972 L 165.077,247.972&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-5&quot; data-label-kind=&quot;center&quot; x=&quot;46.42&quot; y=&quot;258.31&quot; width=&quot;206.12&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-5&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;149.48&quot; y=&quot;276.01&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;149.48&quot; dy=&quot;0.00&quot;&gt;status/code/logs/stats&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-6&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-6&quot; d=&quot;M 165.077,215.972 L 41.355,215.972 L 41.355,148.736 L 153.161,148.736&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-6&quot; data-label-kind=&quot;center&quot; x=&quot;56.06&quot; y=&quot;159.07&quot; width=&quot;206.12&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-6&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;159.12&quot; y=&quot;176.77&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;159.12&quot; dy=&quot;0.00&quot;&gt;status/code/logs/stats&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-7&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-7&quot; d=&quot;M 153.161,116.736 L 26.706,116.736 L 26.706,33.500 L 167.201,33.500&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-7&quot; data-label-kind=&quot;center&quot; x=&quot;57.12&quot; y=&quot;59.83&quot; width=&quot;206.12&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-7&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;160.18&quot; y=&quot;77.53&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;160.18&quot; dy=&quot;0.00&quot;&gt;status/code/logs/stats&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;rect x=&quot;167.20&quot; y=&quot;8.00&quot; width=&quot;86.75&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.58&quot; y=&quot;37.00&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.58&quot; dy=&quot;0.00&quot;&gt;Hop&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;153.16&quot; y=&quot;107.24&quot; width=&quot;114.88&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.60&quot; y=&quot;136.24&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.60&quot; dy=&quot;0.00&quot;&gt;GitRoot&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;133.87&quot; y=&quot;305.71&quot; width=&quot;154.08&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.91&quot; y=&quot;334.71&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.91&quot; dy=&quot;0.00&quot;&gt;executor cli&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;166.83&quot; y=&quot;370.08&quot; width=&quot;88.32&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;211.00&quot; y=&quot;399.08&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;211.00&quot; dy=&quot;0.00&quot;&gt;cmd&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;165.08&quot; y=&quot;206.47&quot; width=&quot;91.36&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.76&quot; y=&quot;235.47&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.76&quot; dy=&quot;0.00&quot;&gt;exec&lt;/tspan&gt;&lt;/text&gt;&lt;g transform=&quot;translate(210.59 107.24) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(210.68 206.47) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(210.84 305.71) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(210.95 370.08) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(133.87 347.21) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(165.08 247.97) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(153.16 148.74) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(167.20 33.50) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;/svg&gt;
&lt;p&gt;Phew, is that enough now? It should be, but we&amp;rsquo;ll see how it performs in real-time on &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev&quot;&gt;gitroot.dev&lt;/a&gt;. Not everything is finished: for example, the SSH executor doesn’t manage cache yet &lt;a href=&quot;issues/94af-executor-cache.html&quot;&gt;#94af&lt;/a&gt; and can&amp;rsquo;t use the container strategy &lt;a href=&quot;issues/f050-ssh-executor-subCmd.html&quot;&gt;#f050&lt;/a&gt;. You can’t run the executor on other architectures &lt;a href=&quot;issues/4d49-executor-compile-other-archs.html&quot;&gt;#4d49&lt;/a&gt; yet, and I’m sure you’ll soon want a way to clean up temp directories &lt;a href=&quot;issues/647f-clean-executor-temp-dir.html&quot;&gt;#647f&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But hey, let’s celebrate what we’ve built before jumping into the next sprint!&lt;/p&gt;

&lt;h3 id=&quot;plugin-registry&quot;&gt;Plugin registry&lt;/h3&gt;

&lt;p&gt;Until now, explaining GitRoot was always about &amp;ldquo;data in git and the plugin system&amp;rdquo;, but what exactly are the plugins? Where are they? What can they do? All of that was a bit mysterious. With this milestone, I’m answering some of those questions with a first shot at a registry. Be careful, though: this first version is just a &amp;ldquo;hand-written&amp;rdquo; preview of what it could be in the future. If you look at the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/plugins/index.md&quot;&gt;source&lt;/a&gt;, you’ll see that everything is ready to be automated, but nothing is&amp;hellip; yet.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;m not entirely sure what you, as a consumer, want to see here. Probably an explanation of what the plugin does, some docs, and the available versions. But as a plugin creator, you likely want to manage all of that in your own repository instead of the &lt;a href=&quot;plugins/index.html&quot;&gt;gitroot.dev registry&lt;/a&gt;. I chose to keep the registry very light and put the details directly in the &lt;a href=&quot;app/plugins/apex/index.html&quot;&gt;plugin&amp;rsquo;s directory&lt;/a&gt; to give you an idea of what your own repo could look like.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ll need to iterate on this. Even if I choose to embed package details in the registry, I still need to decide how: maybe via a JSON in your repo, maybe in the registry itself, or perhaps inside your package (WASM)? I don&amp;rsquo;t know yet what’s simplest for you and for the registry.&lt;/p&gt;

&lt;p&gt;Anyway, I hope this registry helps you understand what a plugin is and how it shapes your forge. Some are small (&lt;a href=&quot;plugins/name/ladybug.html&quot;&gt;ladybug&lt;/a&gt;), others are massive (&lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex&lt;/a&gt;), and some aren&amp;rsquo;t usable solo as they are &lt;a href=&quot;blog/version-0.4.0.html#plugin-deps&quot;&gt;dependencies&lt;/a&gt; for others (like &lt;a href=&quot;plugins/name/apex_mermaid.html&quot;&gt;apx_mermaid&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Since plugins are now decoupled from GitRoot, I’ve moved all changelogs from the &lt;a href=&quot;CHANGELOG.html&quot;&gt;general&lt;/a&gt; one to the individual plugin directories. This way, I’ll be able to release plugin versions independently of GitRoot (and vice versa). I hope to break the plugin API less often, even if I know I probably will in the near future. All of this paves the way for you to build your own plugins and I hope to see them in the gitroot.dev registry soon. Or, of course, you can just build your own registry!&lt;/p&gt;

&lt;h3 id=&quot;apex-render-branch&quot;&gt;Apex render branch&lt;/h3&gt;

&lt;p&gt;For as long as I can remember, this has been a problem with GitRoot: how to render arbitrary changes, especially deletions, on the web? I had imagined a lot of strategies. The last one was to render inside a directory named after the git tree ID (the abstract representation of a working directory). Then, I’d create a symlink between that tree and a main directory.&lt;/p&gt;

&lt;p&gt;The idea was: if you push a file in a branch, git gives you a tree ID like &lt;code&gt;a654fs&lt;/code&gt;. It would be represented as &lt;code&gt;mydomain.com/myBranch/file -&amp;gt; mydomain.com/tree/a654fs/file&lt;/code&gt;. When you merged that branch, GitRoot would simply symlink the main directory to it: &lt;code&gt;mydomain.com/file -&amp;gt; mydomain.com/tree/a654fs/file&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Elegant, but honestly, a total over-engineered mess. When I actually sat down to code this, I ended up adding just two lines to the &lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex plugin&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; p.currentCommit.Branch &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; p.currentCommit.Branch &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; p.config.defaultBranch {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	path = filepath.&lt;span style=&quot;color:#50fa7b&quot;&gt;Join&lt;/span&gt;(p.config.branchesDir, p.currentCommit.Branch, path)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And&amp;hellip; yeah, that was enough. I&amp;rsquo;ve been overthinking this since the very beginning of GitRoot! Rendering files from branches into a dedicated branches directory&amp;hellip; It&amp;rsquo;s simple. Just like GitRoot should be.&lt;/p&gt;

&lt;p&gt;So, what does it mean in practice? The file is now rendered at &lt;code&gt;myinstance.com/mybranch/index.html&lt;/code&gt; and the graft at &lt;code&gt;myinstance.com/mybranch/grafts/mybranch.html&lt;/code&gt;. How did I not think of something this simple before?&lt;/p&gt;

&lt;h3 id=&quot;plugin-deps&quot;&gt;Plugin deps&lt;/h3&gt;

&lt;p&gt;The catalyst for this development was the &lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex plugin&lt;/a&gt;. At over 3 MB, it was by far the largest plugin. It was packed with configuration, specifically a bunch of booleans to &lt;a href=&quot;issues/8b29-apex-working-tree.html&quot;&gt;toggle features&lt;/a&gt;. Furthermore, I needed to render Markdown in other plugins, like &lt;a href=&quot;plugins/name/pollen.html&quot;&gt;pollen&lt;/a&gt;, to create a proper &lt;a href=&quot;issues/83d8-pollen-content-flux.html&quot;&gt;RSS feed&lt;/a&gt;. It became clear to me: this plugin needed to be split into smaller, more specialized ones.&lt;/p&gt;

&lt;p&gt;So, I started building the &lt;a href=&quot;plugins/name/apex_markdown.html&quot;&gt;apex_markdown plugin&lt;/a&gt;, then &lt;a href=&quot;plugins/name/apex_code.html&quot;&gt;apex_code&lt;/a&gt;, and next week &lt;a href=&quot;plugins/name/apex_mermaid.html&quot;&gt;apex_mermaid&lt;/a&gt;. But what I hadn&amp;rsquo;t fully anticipated was the sheer amount of work required to make it all work together.&lt;/p&gt;

&lt;p&gt;What if a dependency is missing? What if a method is deprecated or removed in a newer version? To handle this, I’ve chosen to make all dependencies entirely optional for now. It’s an easy choice to make today, but I might change my mind tomorrow, after all, there’s a reason most package managers handle version ranges and resolution.&lt;/p&gt;

&lt;p&gt;This new architecture allows for adding tons of features without rebuilding an entire plugin. Imagine adding Org-mode or RST support: you just swap out &lt;a href=&quot;plugins/name/apex_markdown.html&quot;&gt;apex_mardown&lt;/a&gt; instead of touching the whole stack. It also allows other plugins to reuse existing behaviors effortlessly.&lt;/p&gt;

&lt;p&gt;With this in place, I’m not even sure if I’ll keep the &lt;code&gt;report&lt;/code&gt; system, which was an other way of passing information between plugins.&lt;/p&gt;

&lt;h3 id=&quot;sigma-plugin&quot;&gt;Sigma plugin&lt;/h3&gt;

&lt;p&gt;Signed commits should be the default in git today. With SSH signing, it’s easy to do, and everyone should use it because impersonating a name and email is way too easy in git. When I started GitRoot, I made signed commits mandatory before allowing a push. But in fact, making signatures mandatory should be a choice for the instance owner. GitRoot is all about the freedom to choose how your forge works for you.&lt;/p&gt;

&lt;p&gt;Furthermore, a friend told me he preferred signing with PGP and already had his setup ready. So why force him to use a &lt;code&gt;.gitroot/init.sh&lt;/code&gt;? It’s frustrating when you want to test a new forge and you’re forced to run random sh commands, even if it&amp;rsquo;s just to configure your git client.&lt;/p&gt;

&lt;p&gt;With this release, I have completely removed &lt;code&gt;init.sh&lt;/code&gt; from GitRoot itself. But of course, as always, I’ve created a plugin to &amp;ldquo;almost&amp;rdquo; recreate this feature. The only difference is: instead of refusing your commit when you push, the &lt;a href=&quot;plugins/name/stigma.html&quot;&gt;stigma plugin&lt;/a&gt; will simply report unsigned or badly signed commits in your graft.&lt;/p&gt;

&lt;p&gt;Now, you have the choice to accept unsigned commits in your repo. You can even ignore the whole signing topic entirely if you don&amp;rsquo;t install the stigma plugin.&lt;/p&gt;

&lt;p&gt;That said, I’m still convinced that signing commits should remain the default.&lt;/p&gt;

&lt;h3 id=&quot;fs-plugin-sdk&quot;&gt;FS plugin-sdk&lt;/h3&gt;

&lt;p&gt;After finishing all the &lt;a href=&quot;blog/version-0.4.0.html#executor&quot;&gt;executors features&lt;/a&gt;, one thing was still missing: the ability for a plugin to move a file from one path to another. As explained in this &lt;a href=&quot;blog/modifying-API-SDK-plugin.html&quot;&gt;blog post&lt;/a&gt;, even though a plugin sees one single filesystem, GitRoot actually uses three different ones: one for cache (a directory), one for the web (a subdirectory), and a virtual one for the git working directory.&lt;/p&gt;

&lt;p&gt;I was missing several methods in both the Rust and TS SDKs, and the existing API was poorly designed with inconsistent naming. I decided to redesign everything and added a lot of new methods accessible via &lt;code&gt;FS&lt;/code&gt; in the SDK. Now, you can copy, move, delete, replace, and write files in every filesystem. Moving and copying can now be done between different filesystems, making it easy to make a file accessible on the web even if it originated from git or the cache.&lt;/p&gt;

&lt;p&gt;I have marked all old methods as deprecated, and they will be removed in the next version of GitRoot (or more specifically, the plugin-sdk, though they share the same release number for now).&lt;/p&gt;

&lt;h2 id=&quot;what-next&quot;&gt;What next?&lt;/h2&gt;

&lt;p&gt;I have a massive roadmap for GitRoot (in no particular order):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discover GitRoot without installing anything.&lt;/li&gt;
&lt;li&gt;Create your own plugins with ease.&lt;/li&gt;
&lt;li&gt;Transform GitRoot into a registry for (at least) Go, Rust, and NPM.&lt;/li&gt;
&lt;li&gt;Plugins should be usable via PURL.&lt;/li&gt;
&lt;li&gt;Plugins can send/receive HTTP requests.&lt;/li&gt;
&lt;li&gt;Plugins can be scheduled.&lt;/li&gt;
&lt;li&gt;Rebase go-git to take advantage of their performance improvements.&lt;/li&gt;
&lt;li&gt;Generate the plugin SDK automatically.&lt;/li&gt;
&lt;li&gt;Add plugin SDKs for (at least) Zig, Kotlin, and Python.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yeah, I could go on all night. And those are just core GitRoot features. I haven&amp;rsquo;t even mentioned making instances to build projects in hosted environments. I haven&amp;rsquo;t talked about specific plugins (sending a &amp;ldquo;toot&amp;rdquo; on commit, managing secrets with age, translating your app&amp;hellip;). I haven&amp;rsquo;t even touched on federating GitRoot with other GitRoot instances, or with Forgejo, SourceHut, or Radicle.&lt;/p&gt;

&lt;p&gt;All of that is possible, and I’m confident it will be accessible in the future. But &amp;ldquo;when&amp;rdquo;? That’s the real question. If you think you can help in any way (coding, of course, but also management, communication, or just to brainstorm), please &lt;a href=&quot;contact.html&quot;&gt;let me know&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is only the beginning. GitRoot will get better and better with every release.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.4.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.3.0.md</link><title>0.3.0</title><pubDate>2025/12/20</pubDate><description>&lt;h1 id=&quot;from-baby-to-teenager-gitroot-become-a-forge&quot;&gt;From &amp;ldquo;baby to teenager&amp;rdquo; GitRoot become a forge&lt;/h1&gt;

&lt;h2 id=&quot;if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes&lt;/h2&gt;

&lt;p&gt;Previously, GitRoot served as a &lt;strong&gt;Source Code Management (SCM)&lt;/strong&gt; system, allowing you to create Git repositories and manage access permissions. However, GitRoot was never intended to be &lt;em&gt;just&lt;/em&gt; an SCM. With this &lt;strong&gt;v0.3.0&lt;/strong&gt; release, it evolves into a full-fledged &lt;strong&gt;forge&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To activate this new capability on your instance, &lt;a href=&quot;blog/version-0.3.0.html#for-admin-activating-an-executor&quot;&gt;follow the administrator section&lt;/a&gt;. To start using it as a user, &lt;a href=&quot;blog/version-0.3.0.html#for-user-using-the-hop-plugin&quot;&gt;see the user section&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The introduction of &lt;strong&gt;executors&lt;/strong&gt; brings a crucial new feature: the &lt;a href=&quot;blog/version-0.3.0.html#plugin-api-report-and-reporter&quot;&gt;report and reporter plugin API&lt;/a&gt;. This API allows any plugin to provide informational feedback to the user. &lt;strong&gt;Important:&lt;/strong&gt; avoid reporting low-level technical details here (use the &lt;code&gt;log&lt;/code&gt; and &lt;code&gt;logError&lt;/code&gt; APIs for that). The initial implementation uses this feature to display command statistics (CPU, memory, etc.) from the new &lt;code&gt;hop&lt;/code&gt; plugin in a clean table via the &lt;code&gt;grafter&lt;/code&gt; plugin.&lt;/p&gt;

&lt;p&gt;With the new &lt;code&gt;hop&lt;/code&gt; plugin, GitRoot now supports 3 languages (Go, TypeScript, Rust) across 4 compilers (Go, TinyGo, Rust, AssemblyScript). Setting up a development environment for this polyglot stack is complex, and keeping it updated is even harder, unless you have the right tools. For this iteration, I have selected &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt; as the default tool for &lt;a href=&quot;blog/version-0.3.0.html#gitroot-bets-on-mise&quot;&gt;managing the GitRoot dev environment&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Beyond technical features, a new focus has been placed on &lt;a href=&quot;blog/version-0.3.0.html#communication-and-community-building&quot;&gt;communication&lt;/a&gt;. You can now follow daily progress on our new fediverse instance (@forge@gitroot.dev), engage in direct chat on matrix (#gitroot:matrix.org), and read a new blog post detailing concrete ways to contribute today.&lt;/p&gt;

&lt;p&gt;Finally, I discuss &lt;a href=&quot;blog/version-0.3.0.html#apex-a-better-web-experience&quot;&gt;Apex&lt;/a&gt; and many &lt;a href=&quot;blog/version-0.3.0.html#and-others-useful-updates&quot;&gt;other tiny but useful things&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;in-depth-details&quot;&gt;In-depth Details&lt;/h2&gt;

&lt;h3 id=&quot;for-admin-activating-an-executor&quot;&gt;For admin: activating an executor&lt;/h3&gt;

&lt;p&gt;When you update GitRoot to &lt;code&gt;0.3.0&lt;/code&gt; a new configuration block will appear in the &lt;code&gt;.gitroot/forgeConfig.yml&lt;/code&gt; file of your &lt;code&gt;root&lt;/code&gt; repository:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;execconf&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;baremetal&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;bwrap&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;user&lt;/span&gt;: nobody
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;uid&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;65534&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;gid&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;65534&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;robind&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;bind&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;container&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;bin&lt;/span&gt;: podman
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;ssh&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;hosts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;user&lt;/span&gt;: myUSer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;address&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;127.0.0.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;port&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;22&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;publickey&lt;/span&gt;: ssh-ed25519 AAAAC3NzaC1...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can enable multiple executors, but GitRoot will only use the first one enabled, checking them in this order: &lt;code&gt;bareMetal&lt;/code&gt;, &lt;code&gt;bwrap&lt;/code&gt;, &lt;code&gt;container&lt;/code&gt; and then &lt;code&gt;ssh&lt;/code&gt;. If you activate an executor, remember to also add the &lt;code&gt;hop&lt;/code&gt; plugin, as it is the only known plugin designed to utilize them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;bare-metal-simplest-but-most-dangerous&quot;&gt;Bare Metal: simplest but most dangerous&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;bareMetal&lt;/code&gt; executor runs commands directly on the host machine. It requires no external dependencies and should work on any hardware. Be extremely cautious: users of your GitRoot instance will gain the ability to execute arbitrary code on the host.&lt;/p&gt;

&lt;h4 id=&quot;bubblewrap-bwrap-lightweight-sandboxing&quot;&gt;Bubblewrap (bwrap): lightweight sandboxing&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;bwrap&lt;/code&gt; executor introduces a lightweight container (sandbox) between executed commands and the host, similar to the technology used by &lt;a target=&quot;_blank&quot; href=&quot;https://flatpak.org/&quot;&gt;flatpack&lt;/a&gt;. To use it, you must install the &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/containers/bubblewrap&quot;&gt;bubblewrap&lt;/a&gt; application.&lt;/p&gt;

&lt;p&gt;By default, GitRoot configures &lt;code&gt;bwrap&lt;/code&gt; to use the &lt;code&gt;nobody&lt;/code&gt; user for restriction, though this can be changed. You can also define bindings between your host and the commands running inside the container. For example, &lt;code&gt;robind: [&amp;quot;/var/log/caddy/access.log&amp;quot;]&lt;/code&gt; would allow a job to execute &lt;code&gt;cat /var/log/caddy/access.log&lt;/code&gt;.&lt;/p&gt;

&lt;h4 id=&quot;container-heavyweight-but-most-flexible&quot;&gt;Container: heavyweight but most flexible&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;container&lt;/code&gt; executor enables the execution of commands within &lt;a target=&quot;_blank&quot; href=&quot;https://podman.io/&quot;&gt;podman&lt;/a&gt; or &lt;a target=&quot;_blank&quot; href=&quot;https://www.docker.com/&quot;&gt;docker&lt;/a&gt; containers, allowing your users to run any OCI image. The only requirement for the administrator is to install either &lt;a target=&quot;_blank&quot; href=&quot;https://podman.io/docs/installation&quot;&gt;podman&lt;/a&gt; or &lt;a target=&quot;_blank&quot; href=&quot;https://www.docker.com/&quot;&gt;docker&lt;/a&gt;. Remember to change the &lt;code&gt;bin&lt;/code&gt; property to &lt;code&gt;docker&lt;/code&gt; if that is your chosen runtime.&lt;/p&gt;

&lt;p&gt;GitRoot includes a useful feature for containers: if the user provides a path to a &lt;code&gt;Containerfile&lt;/code&gt; or &lt;code&gt;Dockerfile&lt;/code&gt;, GitRoot will automatically build the image before running commands inside it.&lt;/p&gt;

&lt;h4 id=&quot;ssh-remote-execution-alpha&quot;&gt;SSH: remote execution (alpha)&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;ssh&lt;/code&gt; executor allows commands to be executed on a remote host via a standard SSH connection. You simply provide the standard SSH connection details. This executor should be considered the most alpha of all options (and yes, all others are also considered alpha).&lt;/p&gt;

&lt;h3 id=&quot;for-user-using-the-hop-plugin&quot;&gt;For user: using the &lt;code&gt;hop&lt;/code&gt; plugin&lt;/h3&gt;

&lt;p&gt;To execute a build (or command execution), users must activate the &lt;code&gt;hop&lt;/code&gt; plugin and configure its execution rules:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;url&lt;/span&gt;: /home/rmaneschi/projects/gitroot/app/testsuite/../plugins/hop/hop-0.0.1.wasm
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: hop
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;version&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;0.0.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;active&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          - &lt;span style=&quot;color:#ff79c6&quot;&gt;command&lt;/span&gt;: cat
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          &lt;span style=&quot;color:#ff79c6&quot;&gt;build&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          &lt;span style=&quot;color:#ff79c6&quot;&gt;cmds&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            - &lt;span style=&quot;color:#ff79c6&quot;&gt;args&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;                - README.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;              &lt;span style=&quot;color:#ff79c6&quot;&gt;cmd&lt;/span&gt;: cat
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          &lt;span style=&quot;color:#ff79c6&quot;&gt;env&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add all desired commands for the plugin to call in &lt;code&gt;run.write.exec&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then in &lt;code&gt;run.configuration.exec&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;set environment variables via &lt;code&gt;env&lt;/code&gt; (e.g., &lt;code&gt;&amp;quot;MY_ENV=my_val&amp;quot;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;specify the &lt;code&gt;build&lt;/code&gt; property (only for the &lt;code&gt;container&lt;/code&gt; executor) to use an existing image (e.g., &lt;code&gt;docker.io/bash:5.3.3&lt;/code&gt;) or to build one from a file (e.g., &lt;code&gt;/my/path/to/Containerfile&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;define the commands and arguments to execute under &lt;code&gt;cmds&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;plugin-api-report-and-reporter&quot;&gt;Plugin api report and reporter&lt;/h3&gt;

&lt;p&gt;Occasionally, plugins need to communicate important information to the user. For instance, the &lt;code&gt;ladybug&lt;/code&gt; plugin might need to report invalid metadata. In traditional forges, a web interface serves this purpose, but GitRoot operates without a fixed UI.&lt;/p&gt;

&lt;p&gt;Past considerations included sending emails (a solution often disliked by developers) or leveraging external communication methods like RSS via the &lt;code&gt;pollen&lt;/code&gt; plugin (introduced in v0.2.0) or the Fediverse via a Go-to-Social instance. As always, GitRoot aims to provide options rather than dictate a single solution.&lt;/p&gt;

&lt;p&gt;To allow reporting without forcing a communication channel, I&amp;rsquo;ve created two new plugin APIs.&lt;/p&gt;

&lt;p&gt;Report: plugins call this API with a severity level (&lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, or &lt;code&gt;error&lt;/code&gt;) and an array of strings to display. GitRoot stacks all reports for every commit and plugin.&lt;/p&gt;

&lt;p&gt;Reporter API: after reports are collected, GitRoot calls all plugins that implement the &lt;code&gt;Reporter&lt;/code&gt; interface.&lt;/p&gt;

&lt;p&gt;Currently, the &lt;code&gt;grafter&lt;/code&gt; plugin is the only known reporter; it adds the collected reports to the graft output. This two new API opens up a wide range of possibilities for future versions. I have several ideas, but I encourage you to &lt;a href=&quot;contact.html&quot;&gt;share yours&lt;/a&gt;!&lt;/p&gt;

&lt;h3 id=&quot;gitroot-bets-on-mise&quot;&gt;GitRoot bets on mise&lt;/h3&gt;

&lt;p&gt;Managing a diverse development environment that spans Go, TypeScript, and Rust can be a significant hurdle. In this iteration, I have standardized on mise as the official tool for managing the GitRoot dev environment. This choice simplifies installation and ensures all dependencies remain correctly updated for development.&lt;/p&gt;

&lt;p&gt;This choice seems to be a good one, as we welcome a new contributor who has already integrated some common tasks using &lt;code&gt;mise&lt;/code&gt;. Thanks to their efforts, we can now simply run &lt;code&gt;mise run build&lt;/code&gt; to compile GitRoot or &lt;code&gt;mise run test&lt;/code&gt; to execute all tests.&lt;/p&gt;

&lt;h3 id=&quot;communication-and-community-building&quot;&gt;Communication and community building&lt;/h3&gt;

&lt;p&gt;Alongside the technical work for v0.3.0, a focused effort was put into non-technical tasks to improve communication with the community.&lt;/p&gt;

&lt;p&gt;To allow you to follow daily progress and current thoughts, I have set up a &lt;a target=&quot;_blank&quot; href=&quot;https://gotosocial.org/&quot;&gt;GoToSocial&lt;/a&gt; (Mastodon) instance. You can find the official account at &lt;code&gt;@forge@gitroot.dev&lt;/code&gt; or directly at &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev&quot;&gt;https://gts.gitroot.dev&lt;/a&gt;. This is the best place for real-time updates.&lt;/p&gt;

&lt;p&gt;I also established a direct chat room on &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.org/&quot;&gt;matrix&lt;/a&gt;, accessible via &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/#gitroot:matrix.org&quot;&gt;https://matrix.to/#/#gitroot:matrix.org&lt;/a&gt;. Feel free to stop by to ask questions or just say hello.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ve added a &lt;a href=&quot;contact.html&quot;&gt;contact&lt;/a&gt; page to ensure you can easily find a way to reach me. It includes multiple options such as email, mastodon, or matrix for direct chat. Please don&amp;rsquo;t hesitate to come say &amp;ldquo;hi&amp;rdquo;!&lt;/p&gt;

&lt;p&gt;Finally, I published a blog post titled &lt;a href=&quot;blog/how-to-help_en_11-2025.html&quot;&gt;&amp;ldquo;How to help building GitRoot 11-2025&amp;rdquo;&lt;/a&gt;. The purpose of this post is to present you with concrete, immediate contribution opportunities, rather than speculating on future possibilities. If you wish to get involved, this is the place to start.&lt;/p&gt;

&lt;h3 id=&quot;apex-a-better-web-experience&quot;&gt;Apex a better web experience&lt;/h3&gt;

&lt;p&gt;The Apex plugin receives numerous bug fixes and quality of life improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;favicon support: a favicon can now be added via the configuration&lt;/li&gt;
&lt;li&gt;styled 404 page: the 404 error page now correctly uses the site&amp;rsquo;s CSS&lt;/li&gt;
&lt;li&gt;HTML metadata configuration: I have added configuration options for HTML metadata, which is useful for SEO and for including specific tags like &lt;code&gt;og&lt;/code&gt; (Open Graph) or &lt;code&gt;fediverse&lt;/code&gt; metadata&lt;/li&gt;
&lt;li&gt;menu highlighting: the current page in the navigation menu is now highlighted&lt;/li&gt;
&lt;li&gt;lightweight template: added some variables in header and footer templates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Breaking:&lt;/strong&gt; previously, the &lt;code&gt;apex&lt;/code&gt; plugin rendered markdown metadata as small blocks at the top of the page by default. With this version, it will no longer render this metadata unless the markdown page explicitly includes &lt;code&gt;apexRenderMetadatas: true&lt;/code&gt; in its front matter.&lt;/p&gt;

&lt;h3 id=&quot;and-others-useful-updates&quot;&gt;And others useful updates&lt;/h3&gt;

&lt;p&gt;Although this article is already quite long, I want to briefly mention several smaller yet important tasks completed in this release.&lt;/p&gt;

&lt;h4 id=&quot;forgeconf-update&quot;&gt;ForgeConf update&lt;/h4&gt;

&lt;p&gt;The parameters &lt;code&gt;externalsshaddr&lt;/code&gt; (default value &lt;code&gt;ssh://localhost:4545/&lt;/code&gt;) and &lt;code&gt;externalhttpaddr&lt;/code&gt; (default value &lt;code&gt;http://localhost:4546/&lt;/code&gt;) have been added to ensure the display of correct external urls to users. These addresses are notably used by the &lt;code&gt;apex&lt;/code&gt; plugin generate proper public links. They are kept separate from the internal &lt;code&gt;sshaddr&lt;/code&gt; and &lt;code&gt;httpaddr&lt;/code&gt; because your GitRoot instance might be configured to listen internally (on &lt;code&gt;127.0.0.1&lt;/code&gt;) while being reachable externally through a reverse proxy.&lt;/p&gt;

&lt;h4 id=&quot;integrated-updater&quot;&gt;Integrated updater&lt;/h4&gt;

&lt;p&gt;I&amp;rsquo;ve added a preliminary implementation for an integrated updater. This feature will handle breaking changes gracefully. For this release, I&amp;rsquo;ve added some new parameters to &lt;code&gt;forgeConf.yml&lt;/code&gt;, the integrated updater will handle these by setting standard values that should not affect your current user instance setup.&lt;/p&gt;

&lt;h4 id=&quot;rust-and-wasm&quot;&gt;Rust and WASM&lt;/h4&gt;

&lt;p&gt;While integrating Rust, I discovered that the WASM (and specifically WASI Preview 1) target does not permit exporting the standard &lt;code&gt;free&lt;/code&gt; and &lt;code&gt;malloc&lt;/code&gt; functions, which GitRoot requires. I&amp;rsquo;ve added an option to rename these functions to &lt;code&gt;gitrootAlloc&lt;/code&gt; and &lt;code&gt;gitrootFree&lt;/code&gt;, resolving this compatibility issue.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.3.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.2.0.md</link><title>0.2.0</title><pubDate>2025/09/18</pubDate><description>&lt;h1 id=&quot;version-0-2-0-is-here&quot;&gt;Version 0.2.0 is here&lt;/h1&gt;

&lt;p&gt;In this version, my main focus was on stabilization. I fixed over 14 issues, some of which were identified before version 0.1.0, but a significant proportion were reported by YOU. A big thanks to everyone who took part of it in any way.&lt;/p&gt;

&lt;p&gt;Every time I shared gitroot, I came across new issues with the documentation and binaries, but also questions such as &amp;ldquo;how can I compile it?&amp;rdquo; or &amp;ldquo;what&amp;rsquo;s the next step?&amp;rdquo;. And I&amp;rsquo;m delighted to see your enthusiasm for it!&lt;/p&gt;

&lt;h2 id=&quot;first-contribution&quot;&gt;First contribution&lt;/h2&gt;

&lt;p&gt;Although all my new contributions were related to &amp;ldquo;bug fixes&amp;rdquo;, some of them required new features.&lt;/p&gt;

&lt;h3 id=&quot;no-notification-no-contribution&quot;&gt;No notification == no contribution&lt;/h3&gt;

&lt;p&gt;The first amusing problem came from the first contributor! Fifteen days later, he contacted me to say, &amp;ldquo;Hi, I pushed a branch where I corrected a lot of typos, but&amp;hellip; nothing has happened since I pushed it?&amp;rdquo;. In fact, I wasn&amp;rsquo;t actually aware that a new branch had been pushed. I had forgotten to implement a mechanism that would notify me whenever a new contribution is published!&lt;/p&gt;

&lt;p&gt;A simple way to achieve this is to add an RSS feed (yes, I was inspired by Radicle for this feature). This is ideal, because all I have to do is add an &amp;ldquo;rss.xml&amp;rdquo; file to the web folder&amp;hellip;and the current API of plugin 0.1.0 allows me to do this! So I created the &lt;code&gt;pollen&lt;/code&gt; plugin. It takes all the commits from all branches and adds an &lt;code&gt;rss:item&lt;/code&gt; to a page.&lt;/p&gt;

&lt;p&gt;However, in order to achieve this, I opted to experiment with a different programming language (all the plugins are developed in Golang). I chose &lt;code&gt;assemblyscript&lt;/code&gt;, which is a subset of the &lt;code&gt;typescript&lt;/code&gt; language. So I added a new &lt;code&gt;plugin sdk&lt;/code&gt; in typescript.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To follow all gitroot commits follow &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/rss/all-commits.xml&quot;&gt;https://gitroot.dev/rss/all-commits.xml&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;lot-of-bugs&quot;&gt;Lot of bugs&lt;/h3&gt;

&lt;p&gt;During this first contribution, I discovered numerous bugs in the “grafter” plugin. From the &lt;a href=&quot;issues/close/v0.2.0/73e8-graft-diff-not-working.html&quot;&gt;diff rendering&lt;/a&gt; to &lt;a href=&quot;issues/close/v0.2.0/e188-graft-merge-not-wanted.html&quot;&gt;unwanted merging&lt;/a&gt; and &lt;a href=&quot;issues/close/9422-graft-need-3-review.html&quot;&gt;very complicated final merging&lt;/a&gt;, nothing worked! Even the basic GitRoot features were painful to use :D&lt;/p&gt;

&lt;h2 id=&quot;let-s-the-show-continue&quot;&gt;Let&amp;rsquo;s the show continue&lt;/h2&gt;

&lt;h3 id=&quot;no-linux-no-gitroot&quot;&gt;No linux == no gitroot&lt;/h3&gt;

&lt;p&gt;Surprisingly, someone wanted to compile gitroot on a Mac. Well, gitroot is actually written in Go, so there&amp;rsquo;s no problem doing that&amp;hellip; Except that I forgot to publish a fix I made to a dependency. Go couldn&amp;rsquo;t download the right dependency and therefore fails to compile completely&amp;hellip; But everything is OK now! I added an automatic compilation for Mac, you can find this on the &lt;a href=&quot;CHANGELOG.html&quot;&gt;version&lt;/a&gt; page.&lt;/p&gt;

&lt;h3 id=&quot;doc-doc-doc&quot;&gt;Doc, doc&amp;hellip; doc?&lt;/h3&gt;

&lt;p&gt;After that, someone tried to run gitroot on their local computer, as described in the &lt;a href=&quot;doc/tutorials/from_zero_to_hero.html&quot;&gt;document&lt;/a&gt;. However, when version &lt;code&gt;0.1.0&lt;/code&gt; was released, I discovered a bug. Gitroot cannot know where the &lt;code&gt;root&lt;/code&gt; repository is located (because you can change its name). To fix this, I added a configuration file before initialization, which had to be passed on launch&amp;hellip; It&amp;rsquo;s my fault, I forgot to update this tutorial.&lt;/p&gt;

&lt;h3 id=&quot;a-lot-more&quot;&gt;A lot more&lt;/h3&gt;

&lt;p&gt;As you can see, there were many mistakes in the first version. However, they have all been fixed, and, of course, I have added plenty more to keep me busy for the next version!&lt;/p&gt;

&lt;h3 id=&quot;more-technicals-bugs&quot;&gt;More technicals bugs&lt;/h3&gt;

&lt;p&gt;But I also fixed several important issues that were the result of some shortcuts that every project has to take for publication. One was about a &lt;a href=&quot;issues/close/v0.2.0/95b0-plugins-fs-bug.html&quot;&gt;FS bug&lt;/a&gt; when directories were empty, another concerned the &lt;a href=&quot;issues/close/v0.2.0/f1ab-garbage-collecte-plugins.html&quot;&gt;garbage collect plugin&lt;/a&gt; when the plugin had finished running, and many others.&lt;/p&gt;

&lt;h2 id=&quot;simplify-plugin-install&quot;&gt;Simplify plugin install&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;0.1.0&lt;/code&gt; version, to install a plugin you should download it, tell to gitroot where it is and then after push/pull activate/configure it. Now it is way more simple, just give an url, like &lt;code&gt;https://gitroot.dev/releases/0.2.0/pollen-0.0.1.wasm&lt;/code&gt; and optionnally (but encouraged) a checksum &lt;code&gt;sha256: 49cd8e1846b1612a0d5686918b502fe29e070e7bf9613c1ba9a1dfe5417d0f2a&lt;/code&gt;. Gitroot will download, check and run it. Of course you need to activate it. All url and checksum are in the &lt;a href=&quot;CHANGELOG.html&quot;&gt;versions page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have added a mecanisme to check that the plugin you try to run is compatble with your version of GitRoot. So launching &lt;code&gt;ladybug-0.0.1.wasm&lt;/code&gt; in &lt;code&gt;gitroot-0.2.0&lt;/code&gt; will log an error and not run the wasm. As GitRoot is in alpha version all new gitroot version will need plugins update.&lt;/p&gt;

&lt;h2 id=&quot;what-next&quot;&gt;What next?&lt;/h2&gt;

&lt;p&gt;With the exception of any new bugs detected for the next iteration, I would like to focus on the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;creating a plugins marketplace&lt;/li&gt;
&lt;li&gt;adding a new language SDK for plugins (Maybe Rust? Or Zig?)&lt;/li&gt;
&lt;li&gt;rebasing the Git lib fork and applying patches&lt;/li&gt;
&lt;/ul&gt;
</description><guid>https://gitroot.dev//blog/version-0.2.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.1.0.md</link><title>0.1.0</title><pubDate>2025/09/18</pubDate><description>&lt;h1 id=&quot;gitroot-0-1-0-is-deployed&quot;&gt;GitRoot 0.1.0 is deployed 🎉��&lt;/h1&gt;

&lt;h2 id=&quot;gitroot-in-numbers&quot;&gt;GitRoot in numbers:&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;58 commits&lt;/li&gt;
&lt;li&gt;+500 files &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/&quot;&gt;https://gitroot.dev/worktree/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;1 issue in gitlab (closed) &lt;a target=&quot;_blank&quot; href=&quot;https://gitlab.com/gitroot/gitroot/-/issues/1&quot;&gt;https://gitlab.com/gitroot/gitroot/-/issues/1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;29 issues close &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/boards/closed.html&quot;&gt;https://gitroot.dev/boards/closed.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;43 issues open &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/boards/issues.html&quot;&gt;https://gitroot.dev/boards/issues.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;22 pages de doc &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/doc/&quot;&gt;https://gitroot.dev/doc/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;4 licences &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/README.html#license&quot;&gt;https://gitroot.dev/README.html#license&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;4 plugins &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/CHANGELOG.html#plugins&quot;&gt;https://gitroot.dev/CHANGELOG.html#plugins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But if you retain only one, it is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 contributor &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/.gitroot/users.yml.html&quot;&gt;https://gitroot.dev/worktree/.gitroot/users.yml.html&lt;/a&gt; (others are forge and plugins 😭)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So there is only one thig to do&amp;hellip; read the doc and contribute!&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.1.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/security-vulnerability-1.md</link><title>blog/security-vulnerability-1.md</title><pubDate>2026/03/30</pubDate><description>&lt;h1 id=&quot;security-advisory-executor-command-injection&quot;&gt;Security Advisory: Executor Command Injection&lt;/h1&gt;

&lt;h2 id=&quot;1-executive-summary&quot;&gt;1. Executive Summary&lt;/h2&gt;

&lt;p&gt;A vulnerability has been identified within the GitRoot &lt;strong&gt;executors&lt;/strong&gt; system. Under specific conditions, it is possible for a malicious actor to execute unauthorized commands on the host machine. Given the complexity of the required conditions, this vulnerability is not easily exploitable in a standard production environment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Severity:&lt;/strong&gt; Moderate&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Affected Version:&lt;/strong&gt; 0.3.0&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fix Status:&lt;/strong&gt; Scheduled for version 0.4.0&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;2-technical-details&quot;&gt;2. Technical Details&lt;/h2&gt;

&lt;p&gt;The vulnerability stems from the way executors validate commands defined by plugins. Currently, a plugin can declare a benign command in its manifest (e.g., &lt;code&gt;cat&lt;/code&gt;), but execute an arbitrary command during runtime (e.g., &lt;code&gt;rm -rf data/&lt;/code&gt;). GitRoot currently lacks a strict enforcement mechanism to verify that the executed binary matches the declared command.&lt;/p&gt;

&lt;h2 id=&quot;3-impact-analysis&quot;&gt;3. Impact Analysis&lt;/h2&gt;

&lt;p&gt;The level of risk depends on your infrastructure configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;High Risk:&lt;/strong&gt; Users utilizing &lt;code&gt;bareMetal&lt;/code&gt; or &lt;code&gt;ssh&lt;/code&gt; executors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Low Risk:&lt;/strong&gt; Users utilizing &lt;code&gt;bubblewrap&lt;/code&gt; or &lt;code&gt;container&lt;/code&gt; isolation (as the sandbox significantly limits the potential impact on the host).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exploitability Constraints:&lt;/strong&gt;
This vulnerability is not a &amp;ldquo;remote code execution&amp;rdquo; flaw in the traditional sense. Exploitation requires:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Installing a malicious plugin.&lt;/li&gt;
&lt;li&gt;Having write access to the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt; of the root repository.&lt;/li&gt;
&lt;li&gt;Merging a malicious &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt; into the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Currently, creating plugins is undocumented, and the only known plugin using executors is &lt;code&gt;hop&lt;/code&gt;. Since any modification to &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt; requires a merge into the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt;, a standard code review process should easily detect and prevent such malicious entries.&lt;/p&gt;

&lt;h2 id=&quot;4-immediate-mitigations&quot;&gt;4. Immediate Mitigations&lt;/h2&gt;

&lt;p&gt;If you cannot wait for the 0.4.0 release, we recommend applying the following measures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Disable Executors:&lt;/strong&gt; Set your forge configuration for executors to &lt;code&gt;None&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secure Executors:&lt;/strong&gt; Use more secure executors, such as &lt;code&gt;bwrap&lt;/code&gt; or &lt;code&gt;container&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plugin Trust:&lt;/strong&gt; Only install and enable plugins from trusted sources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access Control:&lt;/strong&gt; Strictly monitor and review all changes to &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt;, specifically focusing on authorized system commands.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;5-patch-schedule&quot;&gt;5. Patch Schedule&lt;/h2&gt;

&lt;p&gt;This vulnerability will be fully addressed in &lt;strong&gt;version 0.4.0&lt;/strong&gt;.
We have decided not to release an intermediate 0.3.1 patch, as the attack vector relies on an &amp;ldquo;insider&amp;rdquo; or &amp;ldquo;compromised account&amp;rdquo; scenario that is effectively mitigated by standard code review practices.&lt;/p&gt;

&lt;h2 id=&quot;6-security-contact&quot;&gt;6. Security Contact&lt;/h2&gt;

&lt;p&gt;Transparency and security are core values at GitRoot. If you discover any other potential vulnerabilities or have questions regarding this report, please contact us directly at &lt;a href=&quot;blog/contact@gitroot.dev&quot;&gt;contact@gitroot.dev&lt;/a&gt;.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/security-vulnerability-1.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/modifying-API-SDK-plugin.md</link><title>Modifying the API in the SDK Plugin</title><pubDate>2026/04/06</pubDate><description>&lt;h1 id=&quot;modifying-the-api-in-the-sdk-plugin&quot;&gt;Modifying the API in the SDK Plugin&lt;/h1&gt;

&lt;p&gt;The last feature for version 0.4 is about adding &lt;code&gt;mv&lt;/code&gt; (move) and &lt;code&gt;cp&lt;/code&gt; (copy) functions to the SDK plugin. I&amp;rsquo;m taking advantage of this development to explain how the plumbing works under the hood.&lt;/p&gt;

&lt;p&gt;Right now, the SDK plugin is undocumented because there are a lot of breaking changes coming. Every time I develop a new plugin, I discover better ways to do things, and the SDK needs to adapt.&lt;/p&gt;

&lt;p&gt;For example, when I added &lt;code&gt;apex_code&lt;/code&gt; (a plugin that takes code as a string and returns syntax-highlighted HTML), I wondered: &amp;ldquo;Is this a standalone plugin?&amp;rdquo;. Not really, because there&amp;rsquo;s no file to manipulate, neither in input nor output. Instead, it manages blocks inside an existing file. The SDK wasn&amp;rsquo;t originally built with that use case in mind.&lt;/p&gt;

&lt;p&gt;Anyway, I think explaining how the SDK works through a concrete example will be helpful, if not for you, at least for me.&lt;/p&gt;

&lt;h2 id=&quot;what-is-the-sdk-s-job&quot;&gt;What is the SDK&amp;rsquo;s job?&lt;/h2&gt;

&lt;p&gt;The SDK exists to pass data from GitRoot (the host) to the plugin (the guest). As you know, GitRoot uses WebAssembly (WASM) to manage plugins. Unfortunately, WASM is still a bit low-level to implement: between the host and the guest, only integers can travel.&lt;/p&gt;

&lt;p&gt;Wait, only integers?&lt;/p&gt;

&lt;p&gt;Yes. So, how do we send a string? The host can store any kind of data in the guest&amp;rsquo;s memory. GitRoot stores a string, retrieves the pointer (the memory address) and the size, and sends those two integers to the guest. The guest then retrieves the string from its own memory. It&amp;rsquo;s a complex dance just for a simple string. The SDK is here to hide all that mess.&lt;/p&gt;

&lt;p&gt;When you build a plugin, you just implement an interface. The SDK handles the rest: you receive objects and strings, and you send back data without ever worrying about the underlying WASM pointers.&lt;/p&gt;

&lt;h2 id=&quot;what-does-a-concrete-implementation-look-like&quot;&gt;What does a concrete implementation look like?&lt;/h2&gt;

&lt;p&gt;When I add a new function, I always start with the host side. All functions are located in &lt;a href=&quot;app/server/plugin/runtime.go&quot;&gt;runtime.go&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We need to register the function in &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/wazero/wazero&quot;&gt;wazero&lt;/a&gt;, our WASM runtime. I add two versions because I haven&amp;rsquo;t found a clean way to bridge the difference between AssemblyScript and Go/Rust yet. Hopefully, I&amp;rsquo;ll figure that out one day.&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;r.wazRun.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#50fa7b&quot;&gt;NewFunctionBuilder&lt;/span&gt;().&lt;span style=&quot;color:#50fa7b&quot;&gt;WithFunc&lt;/span&gt;(r.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(&lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;)).&lt;span style=&quot;color:#50fa7b&quot;&gt;Export&lt;/span&gt;(&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;moveFile&amp;#34;&lt;/span&gt;).
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#50fa7b&quot;&gt;NewFunctionBuilder&lt;/span&gt;().&lt;span style=&quot;color:#50fa7b&quot;&gt;WithFunc&lt;/span&gt;(r.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(&lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;)).&lt;span style=&quot;color:#50fa7b&quot;&gt;Export&lt;/span&gt;(&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;moveFileAS&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I implement the builder:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(forAS &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;interface&lt;/span&gt;{} {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; !forAS {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, ptr, size, ptr2, size2 &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#6272a4&quot;&gt;// from ptr/size to string or object
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;			r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromPath, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	} &lt;span style=&quot;color:#ff79c6&quot;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, ptr, ptr2 &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#6272a4&quot;&gt;// from ptr to string or object
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;			r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromPath, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Yeah, I know. As a Gopher, seeing &lt;code&gt;interface{}&lt;/code&gt; as a return type and all these uint32 feels wrong. But hey, we&amp;rsquo;re not here for a code review, we&amp;rsquo;re here to add an API. If you have a cleaner way, feel free to submit a graft!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We want to allow any plugin to move a file. Let&amp;rsquo;s define the internal &lt;code&gt;moveFile&lt;/code&gt; function:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromPath, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#6272a4&quot;&gt;//need to do something...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Actually, there&amp;rsquo;s a catch, GitRoot uses three internal filesystems: one for the git worktree, one for web content, and one for the cache. Even if they are abstracted under one root, the code often expects a specific FS. I&amp;rsquo;ll probably clean this up later, but for now:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#6272a4&quot;&gt;//need to do something...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now your plugin can call &lt;code&gt;moveFile(&amp;quot;cache&amp;quot;, &amp;quot;artifacts/myBin&amp;quot;, &amp;quot;web&amp;quot;, &amp;quot;releases/myBin-v1.0&amp;quot;)&lt;/code&gt; to publish an artifact, or &lt;code&gt;moveFile(&amp;quot;web&amp;quot;, &amp;quot;index.html&amp;quot;, &amp;quot;web&amp;quot;, &amp;quot;old/index.html&amp;quot;)&lt;/code&gt; to back up a file.&lt;/p&gt;

&lt;p&gt;I won&amp;rsquo;t bore you with the implementation logic here. If you&amp;rsquo;re curious, check the &lt;a href=&quot;app/server/plugin/runtime.go&quot;&gt;code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is how the final builder looks, converting pointers to strings:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(forAS &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;interface&lt;/span&gt;{} {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; !forAS {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, fromFsPtr, fromFsSize, fromPathPtr, fromGlobPathSize, toFsPtr, toFsSize, toPathPtr, toPathSize &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;uint64&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile fromFs&amp;#34;&lt;/span&gt;, fromFsPtr, fromFsSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile fromGlobPath&amp;#34;&lt;/span&gt;, fromPathPtr, fromGlobPathSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile toFs&amp;#34;&lt;/span&gt;, toFsPtr, toFsSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile toPath&amp;#34;&lt;/span&gt;, toPathPtr, toPathSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	} &lt;span style=&quot;color:#ff79c6&quot;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, fromFsPtr, fromPathPtr, toFsPtr, toPathPtr &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;uint64&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS fromFs&amp;#34;&lt;/span&gt;, fromFsPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS fromGlobPath&amp;#34;&lt;/span&gt;, fromPathPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS toFs&amp;#34;&lt;/span&gt;, toFsPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS toPath&amp;#34;&lt;/span&gt;, toPathPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L38&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L38&quot;&gt;38&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L39&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L39&quot;&gt;39&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L40&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L40&quot;&gt;40&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L41&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L41&quot;&gt;41&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L42&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L42&quot;&gt;42&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L43&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L43&quot;&gt;43&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When I need to pass complex objects, I use &lt;code&gt;json.Marshal&lt;/code&gt; in GitRoot and &lt;code&gt;json.Unmarshal&lt;/code&gt; in the SDK. It works, but don&amp;rsquo;t get too attached to it, I might change the serialization method in the future.&lt;/p&gt;

&lt;h2 id=&quot;inside-the-sdk&quot;&gt;Inside the SDK&lt;/h2&gt;

&lt;p&gt;Now that the host is ready, we need to implement the other side: the SDK itself. I usually start with Go, then move to Rust and AssemblyScript. Sometimes I forget the others&amp;hellip; shame on me! I&amp;rsquo;m planning to automate this with some boilerplate generation later.&lt;/p&gt;

&lt;p&gt;In the &lt;a href=&quot;app/libs/golang/plugin/model/server.go&quot;&gt;server&lt;/a&gt; interface:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;type&lt;/span&gt; Server &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;interface&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#6272a4&quot;&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;    &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#6272a4&quot;&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make it more &amp;ldquo;user-friendly&amp;rdquo;, I use a struct to represent the filesystem in &lt;a href=&quot;app/libs/golang/plugin/model/fs.go&quot;&gt;model/fs.go&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (g &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;GrFs) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(fromPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; g.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(g.base, fromPath, g.base, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (g &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;GrFs) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileToFs&lt;/span&gt;(fromPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, toFs &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;GrFs, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; g.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(g.base, fromPath, toFs.base, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The actual WASM call happens in &lt;a href=&quot;app/libs/golang/plugin/server.go&quot;&gt;plugin/server.go&lt;/a&gt;, where we transform strings back into pointers:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;// MoveFile implements [model.ServerNeeded].
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (s &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;pServer) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(fromFs model.FsBase, fromPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, toFs model.FsBase, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr, size &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;string&lt;/span&gt;(fromFs))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr2, size2 &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(fromPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr3, size3 &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;string&lt;/span&gt;(toFs))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr4, size4 &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	resPtrSize &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;_moveFile&lt;/span&gt;(ptr, size, ptr2, size2, ptr3, size3, ptr4, size4)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(fromFs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(fromPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(toFs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;ptrSizeToError&lt;/span&gt;(resPtrSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally, the link to the host via &lt;code&gt;go:wasmimport&lt;/code&gt; in &lt;a href=&quot;app/libs/golang/plugin/imports.go&quot;&gt;plugin/imports.go&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;//go:wasmimport gitroot moveFile
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;_moveFile&lt;/span&gt;(fromFsPtr, fromFsSize, fromPathPtr, fromPathSize, toFsPtr, toFsSize, toPathPtr, toPathSize &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) (ptrSize &lt;span style=&quot;color:#8be9fd&quot;&gt;uint64&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;how-to-use-it&quot;&gt;How to use it?&lt;/h2&gt;

&lt;p&gt;Now, any plugin can use this. Here&amp;rsquo;s a simple Go plugin that triggers a build whenever a &lt;code&gt;.go&lt;/code&gt; file is modified and moves the binary to the web folder.&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;package&lt;/span&gt; main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;path/filepath&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;strings&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	gitroot &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;gitroot.dev/libs/golang/plugin&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;gitroot.dev/libs/golang/plugin/model&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;var&lt;/span&gt; defaultRun = []model.PluginRun{{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	Path:   &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*.go&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	When:   model.PluginRunWhenAll,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	Branch: []&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;{&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	Write: model.PluginWrite{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		Web: []model.PluginWriteRight{{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			Path: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;releases/main&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			Can:  model.PluginWriteRightCanAll,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;type&lt;/span&gt; Plugin &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	server model.Server
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    needToBuild &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;Init&lt;/span&gt;(repoName &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, confHasChanged &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;, serializedConf &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;StartCommit&lt;/span&gt;(commit model.Commit) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                  { &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;AddFile&lt;/span&gt;(file model.File) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L38&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L38&quot;&gt;38&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L39&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L39&quot;&gt;39&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L40&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L40&quot;&gt;40&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L41&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L41&quot;&gt;41&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;ModFile&lt;/span&gt;(file model.File) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L42&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L42&quot;&gt;42&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L43&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L43&quot;&gt;43&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L44&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L44&quot;&gt;44&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L45&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L45&quot;&gt;45&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L46&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L46&quot;&gt;46&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;DelFile&lt;/span&gt;(file model.File) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L47&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L47&quot;&gt;47&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L48&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L48&quot;&gt;48&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L49&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L49&quot;&gt;49&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L50&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L50&quot;&gt;50&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L51&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L51&quot;&gt;51&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;EndCommit&lt;/span&gt;(commit model.Commit) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                    { &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L52&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L52&quot;&gt;52&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L53&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L53&quot;&gt;53&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;Finish&lt;/span&gt;() &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L54&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L54&quot;&gt;54&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; p.needToBuild {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L55&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L55&quot;&gt;55&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        res, _ &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; p.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;Exec&lt;/span&gt;(model.Exec{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L56&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L56&quot;&gt;56&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            Cmds: []model.Cmd{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L57&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L57&quot;&gt;57&lt;/a&gt;&lt;/span&gt;&lt;span&gt;                {Cmd: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;go&amp;#34;&lt;/span&gt;, Args: []&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;{&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;build&amp;#34;&lt;/span&gt;}},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L58&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L58&quot;&gt;58&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L59&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L59&quot;&gt;59&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            Artifacts: []&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;{&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L60&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L60&quot;&gt;60&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L61&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L61&quot;&gt;61&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; p.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;Cache&lt;/span&gt;().&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileToFs&lt;/span&gt;(res.Artifacts[&lt;span style=&quot;color:#bd93f9&quot;&gt;0&lt;/span&gt;], p.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;Webcontent&lt;/span&gt;(), filepath.&lt;span style=&quot;color:#50fa7b&quot;&gt;Join&lt;/span&gt;(&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;releases&amp;#34;&lt;/span&gt;, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L62&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L62&quot;&gt;62&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L63&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L63&quot;&gt;63&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L64&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L64&quot;&gt;64&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L65&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L65&quot;&gt;65&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L66&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L66&quot;&gt;66&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;Build&lt;/span&gt;(server model.Server) model.Plugin {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L67&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L67&quot;&gt;67&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;&amp;amp;&lt;/span&gt;Plugin{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L68&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L68&quot;&gt;68&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		server: server,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L69&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L69&quot;&gt;69&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L70&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L70&quot;&gt;70&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L71&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L71&quot;&gt;71&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L72&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L72&quot;&gt;72&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;//go:wasmexport install
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L73&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L73&quot;&gt;73&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L74&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L74&quot;&gt;74&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	gitroot.&lt;span style=&quot;color:#50fa7b&quot;&gt;Register&lt;/span&gt;(defaultRun, Build)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L75&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L75&quot;&gt;75&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;is-that-all&quot;&gt;Is that all?&lt;/h2&gt;

&lt;p&gt;Nope. I still need to update the &lt;a href=&quot;app/libs/rust/plugin/src/fs.rs&quot;&gt;Rust&lt;/a&gt; and &lt;a href=&quot;app/libs/ts/plugin/fs.ts&quot;&gt;AssemblyScript&lt;/a&gt; SDKs and, of course, add &lt;a href=&quot;app/libs/golang/plugin/test/fakeserver.go&quot;&gt;tests&lt;/a&gt;. But this should give you a good idea of how the plumbing works.&lt;/p&gt;

&lt;p&gt;If anything is unclear, &lt;a href=&quot;contact.html&quot;&gt;let me know&lt;/a&gt;!&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/modifying-API-SDK-plugin.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/how-to-help_fr_11-2025.md</link><title>Comment contribuer au développement de GitRoot</title><pubDate>2025/11/18</pubDate><description>&lt;h1 id=&quot;comment-contribuer-au-développement-de-gitroot-11-2025&quot;&gt;Comment contribuer au développement de GitRoot 11-2025&lt;/h1&gt;

&lt;p&gt;Je viens tout juste de lire &lt;a target=&quot;_blank&quot; href=&quot;https://antonin.delpeuch.eu/posts/too-much-time-on-your-hands/&quot;&gt;cette article&lt;/a&gt; et je n&amp;rsquo;arrête pas d&amp;rsquo;y penser. Je le met en perspective des contributeurs de GitRoot. Je suis le seul contributeur (à part mon ami Damien qui a corrigé de nombreuses fautes, merci à lui) sur ce projet. Et comme le montre ce &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge/statuses/01K98BCFEB9V430JD9TFQ32YZN&quot;&gt;toot&lt;/a&gt; j&amp;rsquo;ai un travail pour payer les factures, 1 femme et 2 enfants qui m&amp;rsquo;occupent pratiquement tout le reste du temps. À ça je rajoute GitRoot, pour le moment je m&amp;rsquo;éclate et je tiens bon mais pour combien de temps je vais y arriver ?&lt;/p&gt;

&lt;p&gt;Alors pourquoi pas de contributeurs externes sur GitRoot ?&lt;/p&gt;

&lt;p&gt;Je me dit souvent que le projet est jeune, trop jeune, pour que ça soit accessible à un developpeur externe. Parfois je me dit qu&amp;rsquo;un meilleurs système de ticket serait nécessaire pour avoir un label &amp;ldquo;welcome new contribution&amp;rdquo;. Parfois je me dit qu&amp;rsquo;une fois que j&amp;rsquo;aurais déployé les plugins sdk et fait un market place de plugins, enfin vous pourrez venir m&amp;rsquo;aider à faire que GitRoot soit un vrai projet open-source.&lt;/p&gt;

&lt;p&gt;Mais, car il y a un mais, cette liste ne s&amp;rsquo;arrêtera jamais. Soyons franc il y aura toujours un truc de plus à faire avant que vous puissiez m&amp;rsquo;aider à faire que GitRoot soit au top. J&amp;rsquo;ai donc décidé de faire la chose la plus simple à l&amp;rsquo;heure actuelle pour vous expliquer comment m&amp;rsquo;aider: un article de blog.&lt;/p&gt;

&lt;p&gt;Ici, je vais vous expliquer tout ce qu&amp;rsquo;il est possible de faire en l&amp;rsquo;état actuel du projet. Pas ce qu&amp;rsquo;il sera possible de faire un jour &lt;em&gt;peu être&lt;/em&gt;, mais ce que vous pouvez faire là maintenant. Je réitèrerais ce genre d&amp;rsquo;article tant que nécessaire.&lt;/p&gt;

&lt;h2 id=&quot;pré-requis&quot;&gt;Pré-requis&lt;/h2&gt;

&lt;p&gt;Il n&amp;rsquo;y a pas grand chose à faire pour être prêt à aider sur GitRoot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;vous devez avoir &lt;a target=&quot;_blank&quot; href=&quot;https://git-scm.com/&quot;&gt;git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;git clone ssh://gitroot.dev&lt;/li&gt;
&lt;li&gt;cd gitroot.dev&lt;/li&gt;
&lt;li&gt;./gitroot/init.sh&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si vous souhaitez coder il vous faudra en plus:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/bash/manual/bash.html&quot;&gt;bash&lt;/a&gt; et &lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/make/&quot;&gt;make&lt;/a&gt; (si vous êtes dev il y a de grandes chances que vous les aillez déjà)&lt;/li&gt;
&lt;li&gt;installer &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt;, c&amp;rsquo;est le plus simple pour avoir tous les compilateurs dans les bonnes versions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Et voila vous êtes bon pour commencer à aider. Mais que faire ? Et bien comme tout bon developpeur je vais vous répondre &amp;ldquo;ça dépend&amp;rdquo;. Ça dépend de ce que vous voulez faire ! Je vais lister dessous un ensemble de taches possibles, vous choisissez.&lt;/p&gt;

&lt;h2 id=&quot;avant-de-commencer&quot;&gt;Avant de commencer&lt;/h2&gt;

&lt;p&gt;Avant de vous dire ce que vous pouvez faire, je me dit qu&amp;rsquo;il serait bien de vous expliquer ce que moi je fais. Donc pour cette version 0.3.0 ma priorité absolue est de faire en sorte que GitRoot build GitRoot lui même. Aujourd&amp;rsquo;hui à chaque release je fais les binaires sur ma macine et je les scp. Je veux qu&amp;rsquo;avec cette version 0.3.0 cela ne soit plus le cas.&lt;/p&gt;

&lt;p&gt;Donc, si j&amp;rsquo;arrive à me retenir, je ne devrais pas faire les tickets qui suivent. Vous avez tout le temps dont vous souhaitez pour les faire. Le plus simple est, je pense, de nous retrouver sur &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;matrix channel gitroot&lt;/a&gt; pour en discutter.&lt;/p&gt;

&lt;p&gt;On peut également en parler par mail &lt;a href=&quot;blog/contact@gitroot.dev&quot;&gt;contact@gitroot.dev&lt;/a&gt; ou sur le fediverse &lt;cite class=&quot;informative&quot;&gt;&lt;a href=&quot;#forge@gitrrot.dev&quot;&gt;&lt;sup&gt;[forge@gitrrot.dev]&lt;/sup&gt;&lt;/a&gt;&lt;/cite&gt;(&lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;https://gts.gitroot.dev/@forge&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;qu-est-ce-qu-on-peut-faire&quot;&gt;Qu&amp;rsquo;est-ce qu&amp;rsquo;on peut faire ?&lt;/h2&gt;

&lt;h3 id=&quot;corriger-les-fautes&quot;&gt;Corriger les fautes&lt;/h3&gt;

&lt;p&gt;L&amp;rsquo;anglais n&amp;rsquo;est pas ma langue d&amp;rsquo;origine et je pense qu&amp;rsquo;il y a beaucoup de fautes, donc une tache pourrais être de lire tous les md dans &lt;code&gt;/doc/&lt;/code&gt;, &lt;code&gt;/blog/&lt;/code&gt; et pourquoi pas &lt;code&gt;/issues/&lt;/code&gt;. Vous corrigez, faites une branche et poussez. Je m&amp;rsquo;occupe du reste !&lt;/p&gt;

&lt;h3 id=&quot;moi-je-veux-faire-du-web&quot;&gt;Moi je veux faire du web&lt;/h3&gt;

&lt;p&gt;Ce qui serait top, ce serait de créer (ou de trouver) un thème. Aujourd&amp;rsquo;hui GitRoot utilise &lt;a target=&quot;_blank&quot; href=&quot;https://simplecss.org/&quot;&gt;simplecss&lt;/a&gt;, et il n&amp;rsquo;a besoin que de &lt;code&gt;css classless&lt;/code&gt;, il en existe beaucoup mais vous pouvez aussi créer celui de GitRoot.&lt;/p&gt;

&lt;p&gt;Le plus simple pour essayer est de lancer la commande &lt;code&gt;make testsuite&lt;/code&gt; quand elle a fini rendez vous dans &lt;code&gt;cd /tmp/repo1&lt;/code&gt; éditez le fichier &lt;code&gt;style.css&lt;/code&gt; puis &lt;code&gt;git add .&lt;/code&gt; &lt;code&gt;git commit -m &amp;quot;try style&amp;quot;&lt;/code&gt; et &lt;code&gt;git push&lt;/code&gt;, ouvrez &lt;a target=&quot;_blank&quot; href=&quot;http://127.0.0.1:4546/repo1&quot;&gt;http://127.0.0.1:4546/repo1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Quand vous êtes content faites une branche avec votre css dans &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/resources/styles/&quot;&gt;apex&lt;/a&gt;, puis &lt;code&gt;git add .&lt;/code&gt; et &lt;code&gt;git push&lt;/code&gt; je m&amp;rsquo;occupe du reste.&lt;/p&gt;

&lt;h3 id=&quot;moi-je-veux-coder&quot;&gt;Moi je veux coder&lt;/h3&gt;

&lt;p&gt;Bien, pas de soucis, voici une liste de petites tâches (enfin elles me paraissent petites mais si c&amp;rsquo;est pas le cas pour vous dites le moi):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;en golang:

&lt;ul&gt;
&lt;li&gt;dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/grafter/&quot;&gt;grafter&lt;/a&gt; pouvoir afficher &lt;a href=&quot;issues/3d33-grafter-dco.html&quot;&gt;autre chose&lt;/a&gt; dans le 1er message d&amp;rsquo;un graft. La bannière est en dure dans le plugin, il faudrait l&amp;rsquo;exporter dans la conf du plugin et l&amp;rsquo;utiliser.&lt;/li&gt;
&lt;li&gt;dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/silo/&quot;&gt;silo&lt;/a&gt; permettre de &lt;a href=&quot;issues/76ec-silo-more-selects.html&quot;&gt;grouper par fichier ou date&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;plus difficile mais j&amp;rsquo;aimerais bien avoir plus de &lt;a href=&quot;issues/e6d8-apex-alert-support.html&quot;&gt;renderer&lt;/a&gt; dans &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/&quot;&gt;apex&lt;/a&gt;, par exemple les infos, warning et autre&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;en typescript:

&lt;ul&gt;
&lt;li&gt;dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; il y a un bug sur le rss &lt;a href=&quot;issues/3986-pollen-rss-grow-infinite.html&quot;&gt;il est jamais nettoyé&lt;/a&gt;. Rajouté une option (ou pas, 100 en dur me semble être déjà pas mal) pour limiter le nombre d&amp;rsquo;&lt;code&gt;&amp;lt;item&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;toujours dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; pourquoi pas rajouter un &lt;a target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/JSON_Feed&quot;&gt;json feed&lt;/a&gt; en plus du rss. On peut serializer du json en assemblescript.&lt;/li&gt;
&lt;li&gt;plus difficile mais il serait bien de pouvoir dire au plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; de générer un rss avec le contenu des fichiers. Ça permettrait à GitRoot d&amp;rsquo;avoir un rss affichant les articles du blog par exemple. Aujourd&amp;rsquo;hui il ne met dans le rss que les commits.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;en rust:

&lt;ul&gt;
&lt;li&gt;faire ce que Ygor à dit dans &lt;a target=&quot;_blank&quot; href=&quot;https://floss.social/@ygor/115474535138702961&quot;&gt;https://floss.social/@ygor/115474535138702961&lt;/a&gt; je ne suis pas un developpeur rust et transformer des &lt;code&gt;String&lt;/code&gt; en &lt;code&gt;&amp;amp;str&lt;/code&gt; me parle pas beaucoup mais si vous l&amp;rsquo;êtes je suis sûr que c&amp;rsquo;est une petite tâche. Le code se trouve dans le &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; et le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/hop/&quot;&gt;hop&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;ajouter un &lt;a href=&quot;issues/9f00-rust-sdk-fake-server-test.html&quot;&gt;fakeServer&lt;/a&gt; au &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; pour faciliter les tests d&amp;rsquo;un plugin, voici celui de go &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&quot;&gt;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;moi-je-suis-sysadmin&quot;&gt;Moi je suis sysadmin&lt;/h3&gt;

&lt;p&gt;Parfait il y a de quoi faire aussi:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;aujourd&amp;rsquo;hui il y a trop d&amp;rsquo;étapes pour releaser GitRoot, &lt;a href=&quot;issues/dba0-simplify-release.html&quot;&gt;automatiser&lt;/a&gt; au maximum m&amp;rsquo;aiderais beaucoup.&lt;/li&gt;
&lt;li&gt;si vous avez un petit de place sur un de vos serveurs, &lt;a href=&quot;doc/tutorials/deploy_instance.html&quot;&gt;tester de déployer une instance&lt;/a&gt; juste pour le fun. Peu être vous pourrez améliorer la doc ou trouver quelque chose qui ne marche plus. Ou ajouter un paragraphe pour utiliser gitroot avec nginx ou autre.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;moi-je-veux-tester&quot;&gt;Moi je veux tester&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;tous les tests sont en bash dans &lt;a href=&quot;app/testsuite/runtest.sh&quot;&gt;testsuite/runtest.sh&lt;/a&gt; se trouve le plus gros test, à côté il y en d&amp;rsquo;autres

&lt;ul&gt;
&lt;li&gt;exécutez les pour voir s&amp;rsquo;ils marchent chez vous me parait une bonne première tâche&lt;/li&gt;
&lt;li&gt;en créer de nouveaux&lt;/li&gt;
&lt;li&gt;améliorer les existants en sortant par exemple des fonctions helper comme &lt;code&gt;quiet_git()&lt;/code&gt;, &lt;code&gt;report()&lt;/code&gt; ou &lt;code&gt;mySleep()&lt;/code&gt; dans d&amp;rsquo;autres fichiers (et les importer) serait d&amp;rsquo;une grande aide pour le projet. Vous pouvez aussi créer des nouveaux pour &lt;code&gt;killGitroot()&lt;/code&gt; qui est fait en début de chaque fichier. Ou &lt;code&gt;createProject()&lt;/code&gt; qui consiste à chaque fois à aller dans le root repository et ajouter son nom dans le fichier &lt;code&gt;.gitroot/repositories.yml&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;moi-je-veux-juste-réfléchir-discutter&quot;&gt;Moi je veux juste réfléchir/discutter&lt;/h3&gt;

&lt;p&gt;Tu es le bienvenue sur le &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;channel gitroot&lt;/a&gt; dans matrix. Ou par mail &lt;code&gt;contact [AT] gitroot.dev&lt;/code&gt; ou sur le fediverse &lt;code&gt;@forge@gitroot.dev&lt;/code&gt; &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;sur ce serveur&lt;/a&gt;.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/how-to-help_fr_11-2025.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/how-to-help_en_11-2025.md</link><title>How to help building GitRoot</title><pubDate>2025/11/18</pubDate><description>&lt;h1 id=&quot;how-to-help-building-gitroot-11-2025&quot;&gt;How to help building GitRoot 11-2025&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;This article is the translation of &lt;a href=&quot;blog/how-to-help_fr_11-2025.html&quot;&gt;french version&lt;/a&gt; thanks to &lt;a target=&quot;_blank&quot; href=&quot;https://www.deepl.com&quot;&gt;DeepL&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I just read &lt;a target=&quot;_blank&quot; href=&quot;https://antonin.delpeuch.eu/posts/too-much-time-on-your-hands/&quot;&gt;this article&lt;/a&gt; and I can&amp;rsquo;t stop thinking about it. I&amp;rsquo;m putting it into perspective for GitRoot contributors. I&amp;rsquo;m the only contributor (apart from my friend Damien, who corrected numerous typos, thank you to him) on this project. And as this &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge/statuses/01K98BCFEB9V430JD9TFQ32YZN&quot;&gt;toot&lt;/a&gt; shows, I have a job to pay the bills, a wife and two children who take up practically all the rest of my time. On top of that, I have GitRoot. For now, I&amp;rsquo;m having a blast and hanging in there, but how long can I keep it up?&lt;/p&gt;

&lt;p&gt;So why not have external contributors on GitRoot?&lt;/p&gt;

&lt;p&gt;I often tell myself that the project is young, too young, to be accessible to an external developer. Sometimes I think that a better issues system would be necessary to have a “welcome new contribution” label. Sometimes I think that once I&amp;rsquo;ve deployed the SDK plugins and created a plugin marketplace, then you can come and help me make GitRoot a true open-source project.&lt;/p&gt;

&lt;p&gt;But, because there is a but, this list will never end. Let&amp;rsquo;s be honest, there will always be something else to do before you can help me make GitRoot the best it can be. So I decided to do the simplest thing possible right now to explain how you can help me: a blog post.&lt;/p&gt;

&lt;p&gt;Here, I&amp;rsquo;ll explain everything that can be done in the current state of the project. Not what might be possible someday, but what you can do right now. I&amp;rsquo;ll repeat this kind of article as often as necessary.&lt;/p&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;There isn&amp;rsquo;t much you need to do to get ready to help out on GitRoot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you need to have &lt;a target=&quot;_blank&quot; href=&quot;https://git-scm.com/&quot;&gt;git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;git clone ssh://gitroot.dev&lt;/li&gt;
&lt;li&gt;cd gitroot.dev&lt;/li&gt;
&lt;li&gt;./gitroot/init.sh&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to code, you will also need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/bash/manual/bash.html&quot;&gt;bash&lt;/a&gt; and &lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/make/&quot;&gt;make&lt;/a&gt; (if you are a developer, chances are you already have them)&lt;/li&gt;
&lt;li&gt;Install &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt;, which is the easiest way to get all the right versions of the compilers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And now you&amp;rsquo;re ready to start helping. But what should you do? Well, like any good developer, I&amp;rsquo;ll answer, &amp;ldquo;It depends.&amp;rdquo;. It depends on what you want to do! I&amp;rsquo;ll list a set of possible tasks below, and you can choose.&lt;/p&gt;

&lt;h2 id=&quot;before-we-begin&quot;&gt;Before we begin&lt;/h2&gt;

&lt;p&gt;Before telling you what you can do, I thought it would be a good idea to explain what I am doing. So, for this version 0.3.0, my top priority is to make sure that GitRoot builds GitRoot itself. Currently, for each release, I build the binaries on my machine and then scp them. I want this to no longer be the case with version 0.3.0.&lt;/p&gt;

&lt;p&gt;So, if I can restrain myself, I shouldn&amp;rsquo;t have to do the following tickets. You have all the time you need to do them. The easiest thing, I think, is to meet up on &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;matrix channel gitroot&lt;/a&gt; to discuss them.&lt;/p&gt;

&lt;p&gt;We can also talk about it by email &lt;a href=&quot;blog/contact@gitroot.dev&quot;&gt;contact@gitroot.dev&lt;/a&gt; or on the fediverse &lt;cite class=&quot;informative&quot;&gt;&lt;a href=&quot;#forge@gitrrot.dev&quot;&gt;&lt;sup&gt;[forge@gitrrot.dev]&lt;/sup&gt;&lt;/a&gt;&lt;/cite&gt;(&lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;https://gts.gitroot.dev/@forge&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;what-can-we-do&quot;&gt;What can we do?&lt;/h2&gt;

&lt;h3 id=&quot;correct-mistakes&quot;&gt;Correct mistakes&lt;/h3&gt;

&lt;p&gt;English is not my native language, and I think there are a lot of typos, so one task could be to read all the MD files in &lt;code&gt;/doc/&lt;/code&gt;, &lt;code&gt;/blog/&lt;/code&gt;, and maybe even &lt;code&gt;/issues/&lt;/code&gt;. Correct them, create a branch, and push. I&amp;rsquo;ll take care of the rest!&lt;/p&gt;

&lt;h3 id=&quot;i-want-to-work-on-the-web&quot;&gt;I want to work on the web&lt;/h3&gt;

&lt;p&gt;It would be great to create (or find) a theme. GitRoot currently uses &lt;a target=&quot;_blank&quot; href=&quot;https://simplecss.org/&quot;&gt;simplecss&lt;/a&gt;, and it only needs &lt;code&gt;classless CSS&lt;/code&gt;. There are many available, but you can also create one for GitRoot.&lt;/p&gt;

&lt;p&gt;The easiest way to try it out is to run the &lt;code&gt;make testsuite&lt;/code&gt; command. When it&amp;rsquo;s finished, go to &lt;code&gt;cd /tmp/repo1&lt;/code&gt;, edit the &lt;code&gt;style.css&lt;/code&gt; file, then &lt;code&gt;git add .&lt;/code&gt; &lt;code&gt;git commit -m &amp;quot;try style&amp;quot;&lt;/code&gt; and &lt;code&gt;git push&lt;/code&gt;. Open &lt;a target=&quot;_blank&quot; href=&quot;http://127.0.0.1:4546/repo1&quot;&gt;http://127.0.0.1:4546/repo1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When you are satisfied, create a branch with your CSS in &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/resources/styles/&quot;&gt;apex&lt;/a&gt;, then &lt;code&gt;git add .&lt;/code&gt; and &lt;code&gt;git push&lt;/code&gt;. I&amp;rsquo;ll take care of the rest.&lt;/p&gt;

&lt;h3 id=&quot;i-want-to-code&quot;&gt;I want to code&lt;/h3&gt;

&lt;p&gt;Okay, no worries, here&amp;rsquo;s a list of small tasks (well, they seem small to me, but if that&amp;rsquo;s not the case for you, let me know):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;in golang:

&lt;ul&gt;
&lt;li&gt;in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/grafter/&quot;&gt;grafter&lt;/a&gt; plugin, be able to display &lt;a href=&quot;issues/3d33-grafter-dco.html&quot;&gt;something else&lt;/a&gt; in the first message of a graft. The banner is hard-coded in the plugin; it would need to be exported to the plugin configuration and used.&lt;/li&gt;
&lt;li&gt;In the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/silo/&quot;&gt;silo&lt;/a&gt; plugin, allow &lt;a href=&quot;issues/76ec-silo-more-selects.html&quot;&gt;grouping by file or date&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;More difficult, but I would like to have more &lt;a href=&quot;issues/e6d8-apex-alert-support.html&quot;&gt;renderers&lt;/a&gt; in &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/&quot;&gt;apex&lt;/a&gt;, for example info, warnings, and others&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;in typescript:

&lt;ul&gt;
&lt;li&gt;in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; plugin there is a bug in the RSS feed &lt;a href=&quot;issues/3986-pollen-rss-grow-infinite.html&quot;&gt;it is never cleaned up&lt;/a&gt;. Added an option (or not, 100 seems fine to me) to limit the number of &lt;code&gt;&amp;lt;item&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Still in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; plugin, why not add a &lt;a target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/JSON_Feed&quot;&gt;json feed&lt;/a&gt; in addition to the RSS feed? We can serialize json in assemblescript.&lt;/li&gt;
&lt;li&gt;More difficult, but it would be nice to be able to tell the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; plugin to generate an RSS feed with the contents of the files. This would allow GitRoot to have an RSS feed displaying blog articles, for example. Currently, it only puts commits in the RSS feed.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;in Rust:

&lt;ul&gt;
&lt;li&gt;Do what Ygor said in &lt;a target=&quot;_blank&quot; href=&quot;https://floss.social/@ygor/115474535138702961&quot;&gt;https://floss.social/@ygor/115474535138702961&lt;/a&gt; I&amp;rsquo;m not a Rust developer and converting &lt;code&gt;String&lt;/code&gt; to &lt;code&gt;&amp;amp;str&lt;/code&gt; doesn&amp;rsquo;t mean much to me, but if you are, I&amp;rsquo;m sure it&amp;rsquo;s a small task. The code can be found in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; and the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/hop/&quot;&gt;hop&lt;/a&gt; plugin.&lt;/li&gt;
&lt;li&gt;Add a &lt;a href=&quot; issues/9f00-rust-sdk-fake-server-test.html&quot;&gt;fakeServer&lt;/a&gt; to the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; to facilitate plugin testing. Here is the one for Go &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&quot;&gt;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;i-m-a-sysadmin&quot;&gt;I&amp;rsquo;m a sysadmin.&lt;/h3&gt;

&lt;p&gt;Perfect, there&amp;rsquo;s plenty to do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Today, there are too many steps to release GitRoot. &lt;a href=&quot;issues/dba0-simplify-release.html&quot;&gt;Automating&lt;/a&gt; as much as possible would help me a lot.&lt;/li&gt;
&lt;li&gt;If you have a little space on one of your servers, &lt;a href=&quot;doc/tutorials/deploy_instance.html&quot;&gt;try deploying an instance&lt;/a&gt; just for fun. Maybe you can improve the documentation or find something that no longer works. Or add a paragraph on using gitroot with nginx or something else.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;i-want-to-test&quot;&gt;I want to test&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;All tests are in bash in &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/testsuite/&quot;&gt;/app/testsuite/&lt;/a&gt;. The largest test is located in &lt;code&gt;runtest.sh&lt;/code&gt;, along with others.

&lt;ul&gt;
&lt;li&gt;Running them to see if they work on your system seems like a good first task to me.&lt;/li&gt;
&lt;li&gt;Creating new ones&lt;/li&gt;
&lt;li&gt;Improving existing ones by, for example, moving helper functions such as &lt;code&gt;quiet_git()&lt;/code&gt;, &lt;code&gt;report()&lt;/code&gt;, or &lt;code&gt;mySleep()&lt;/code&gt; to other files (and importing them) would be a great help to the project. You can also create new ones for &lt;code&gt;killGitroot()&lt;/code&gt;, which is done at the beginning of each file. Or &lt;code&gt;createProject()&lt;/code&gt;, which consists of going to the root repository each time and adding its name to the &lt;code&gt;.gitroot/repositories.yml&lt;/code&gt; file&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;i-just-want-to-think-discuss&quot;&gt;I just want to think/discuss&lt;/h3&gt;

&lt;p&gt;You are welcome to join the &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;gitroot channel&lt;/a&gt; on Matrix. Or contact us by email at &lt;code&gt;contact [AT] gitroot.dev&lt;/code&gt; or on the Fediverse at &lt;code&gt;@forge@gitroot.dev&lt;/code&gt; &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;on this server&lt;/a&gt;.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/how-to-help_en_11-2025.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/issues.md</link><title>Issues, Tickets, Discussions... Call them what you want!</title><pubDate>2026/06/08</pubDate><description>&lt;h1 id=&quot;issues-tickets-discussions-call-them-what-you-want&quot;&gt;Issues, Tickets, Discussions&amp;hellip; Call them what you want!&lt;/h1&gt;

&lt;p&gt;Many of you, when checking out &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev&quot;&gt;https://gitroot.dev&lt;/a&gt;, have told me that the &lt;code&gt;🐞 Issues&lt;/code&gt; button shouldn&amp;rsquo;t be labeled &amp;ldquo;Issues&amp;rdquo;, but rather &amp;ldquo;Tickets&amp;rdquo; or &amp;ldquo;Discussions&amp;rdquo;.&lt;/p&gt;

&lt;p&gt;First of all, thanks for the feedback! I really appreciate it, that&amp;rsquo;s how I can build a better forge. Of course, I will change the label, but developing a forge means having a lot of different areas to work on: issues/boards, git, code, CI/CD, and with GitRoot, even more, like plugins and WASM&amp;hellip;&lt;/p&gt;

&lt;p&gt;So, why don&amp;rsquo;t I just change this label right away? In reality, this label isn&amp;rsquo;t hardcoded inside GitRoot. It&amp;rsquo;s only there because I configured plugins to display it. I want to take this opportunity to explain how GitRoot works, because the platform is developed around this exact concept: how to customize a forge at the project level.&lt;/p&gt;

&lt;h2 id=&quot;the-original-idea&quot;&gt;The Original Idea&lt;/h2&gt;

&lt;p&gt;Imagine you are developing a forge, and to avoid vendor lock-in, you decide not to put anything outside of Git. You choose not to use any database at all. In this context, how do you design an &amp;ldquo;issue&amp;rdquo; system?&lt;/p&gt;

&lt;p&gt;In traditional forges, an &amp;ldquo;issue&amp;rdquo; consists of a title and a description created by a user. We can represent it like this:&lt;/p&gt;
&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;379.68002&quot; height=&quot;332.2&quot; viewBox=&quot;0 0 379.68002 332.2&quot;&gt;&lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;379.68002&quot; height=&quot;332.2&quot; fill=&quot;transparent&quot;/&gt;&lt;defs&gt;&lt;marker id=&quot;arrow-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 10 5 L 0 10 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-start-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;4.5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 5 L 10 10 L 10 0 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-open-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;1&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1 7 L 18 13 V 1 Z&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-open-start-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;18&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1 7 L 18 13 V 1 Z&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-dep-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;13&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 18 7 L 9 13 L 14 7 L 9 1 Z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-dep-start-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;6&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 5 7 L 9 13 L 1 7 L 9 1 Z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;path id=&quot;edge-0&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-0&quot; d=&quot;M 135.960,121.250 L 96.540,175.250&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot; x=&quot;85.37&quot; y=&quot;134.25&quot; width=&quot;61.76&quot; height=&quot;28.00&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.85&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.35&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;116.25&quot; y=&quot;151.75&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;116.25&quot; dy=&quot;0.00&quot;&gt;Author&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-1&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-1&quot; d=&quot;M 231.960,121.250 L 255.380,175.250&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-1&quot; data-label-kind=&quot;center&quot; x=&quot;217.27&quot; y=&quot;134.25&quot; width=&quot;52.80&quot; height=&quot;28.00&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.85&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.35&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-1&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;243.67&quot; y=&quot;151.75&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;243.67&quot; dy=&quot;0.00&quot;&gt;State&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;rect x=&quot;87.90&quot; y=&quot;8.00&quot; width=&quot;192.12&quot; height=&quot;113.25&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;line x1=&quot;93.90&quot; y1=&quot;46.95&quot; x2=&quot;274.02&quot; y2=&quot;46.95&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1.0&quot;/&gt;&lt;text x=&quot;183.96&quot; y=&quot;34.00&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;183.96&quot; dy=&quot;0.00&quot; font-weight=&quot;600&quot;&gt;Issue&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;117.90&quot; y=&quot;69.70&quot; text-anchor=&quot;start&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;117.90&quot; dy=&quot;0.00&quot;&gt;int id&lt;/tspan&gt;&lt;tspan x=&quot;117.90&quot; dy=&quot;17.85&quot;&gt;string title&lt;/tspan&gt;&lt;tspan x=&quot;117.90&quot; dy=&quot;17.85&quot;&gt;string description&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;203.08&quot; y=&quot;175.25&quot; width=&quot;168.60&quot; height=&quot;148.95&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;line x1=&quot;209.08&quot; y1=&quot;214.20&quot; x2=&quot;365.68&quot; y2=&quot;214.20&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1.0&quot;/&gt;&lt;text x=&quot;287.38&quot; y=&quot;201.25&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;287.38&quot; dy=&quot;0.00&quot; font-weight=&quot;600&quot;&gt;Status&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;233.08&quot; y=&quot;236.95&quot; text-anchor=&quot;start&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;0.00&quot;&gt;&amp;lt;&amp;lt;enumeration&amp;gt;&amp;gt;&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;Open&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;TODO&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;DOING&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;CLOSE&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;8.00&quot; y=&quot;175.25&quot; width=&quot;145.08&quot; height=&quot;95.40&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;line x1=&quot;14.00&quot; y1=&quot;214.20&quot; x2=&quot;147.08&quot; y2=&quot;214.20&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1.0&quot;/&gt;&lt;text x=&quot;80.54&quot; y=&quot;201.25&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;80.54&quot; dy=&quot;0.00&quot; font-weight=&quot;600&quot;&gt;User&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;38.00&quot; y=&quot;236.95&quot; text-anchor=&quot;start&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;38.00&quot; dy=&quot;0.00&quot;&gt;int id&lt;/tspan&gt;&lt;tspan x=&quot;38.00&quot; dy=&quot;17.85&quot;&gt;string email&lt;/tspan&gt;&lt;/text&gt;&lt;/svg&gt;
&lt;p&gt;Easy, right? Well, how do you represent that in GitRoot using only files? This question was running through my mind for months before I finally found a solution.&lt;/p&gt;

&lt;h2 id=&quot;the-basic-solution&quot;&gt;The Basic Solution&lt;/h2&gt;

&lt;p&gt;Since I only have files at my disposal, what actually constitutes an &amp;ldquo;issue&amp;rdquo;? I need to add some metadata to a file so the system understands that this file is an issue and not, say, a &lt;code&gt;main.go&lt;/code&gt;. I’ll probably want to group all these &amp;ldquo;issues&amp;rdquo; into a specific directory, because while GitRoot technically lets you do whatever you want (even dropping an issue right in the middle of your source code), I personally prefer to keep my files organized!&lt;/p&gt;

&lt;p&gt;A simple text format that handles metadata perfectly is Markdown. I use Markdown here, but any other markup language could work. Let&amp;rsquo;s create a file named &lt;code&gt;issues/1.md&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;id: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;user: email@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;status: OPEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# My title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt;9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;My description where I can detail what my problem is.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yes, it really is that simple. In GitRoot, to create an issue, you just create a file, add some metadata, commit, and push. Boom, you have an issue.&lt;/p&gt;

&lt;p&gt;Now, you might be thinking: &amp;ldquo;Well, I lose a lot of control with that, the status could be anything!&amp;rdquo; or &amp;ldquo;How are other users supposed to know what to put where?&amp;rdquo;. You&amp;rsquo;re right, and that is exactly where plugins enter the game.&lt;/p&gt;

&lt;h2 id=&quot;a-better-solution&quot;&gt;A Better Solution&lt;/h2&gt;

&lt;p&gt;If I simplify this as much as possible, what does an external contributor actually need to provide to create an issue? Just a title and a description. So, let&amp;rsquo;s create a file named &lt;code&gt;issues/my_issue.md&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# My title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;My description where I can detail what my problem is.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we run &lt;code&gt;git commit &amp;amp;&amp;amp; git push&lt;/code&gt;. Since I have configured the &lt;a href=&quot;plugins/name/ladybug.html&quot;&gt;ladybug plugin&lt;/a&gt; like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: ladybug
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: issues/**/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;func&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: issues/**/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;callfunc&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;default&lt;/span&gt;: autogenerated
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;mandatory&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: id
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;type&lt;/span&gt;: crc16
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;default&lt;/span&gt;: OPEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;mandatory&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: status
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;type&lt;/span&gt;: string
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;default&lt;/span&gt;: email@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;mandatory&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: user
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;type&lt;/span&gt;: string
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can read this configuration as: &amp;ldquo;For every Markdown file in the &lt;code&gt;issues&lt;/code&gt; directory across all branches, if the file doesn&amp;rsquo;t have these metadata fields, automatically add these default values.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, if you are a Git purist, a server creating commits right on top of yours might sound a bit radical, or even terrifying. But look closely at the &lt;code&gt;write&lt;/code&gt; block in the YAML configuration above:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: issues/**/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;GitRoot doesn&amp;rsquo;t just let plugins run wild, the project configuration must explicitly grant the plugin permission to write or modify files on specific paths. If you don&amp;rsquo;t declare these rights, GitRoot will block the plugin from making any changes.&lt;/p&gt;

&lt;p&gt;When granted, GitRoot intercepts your push, lets the plugin inject the missing metadata, and safely generates a follow-up commit. And don&amp;rsquo;t worry about your history: if you decide to &lt;code&gt;git rebase -i&lt;/code&gt; later to squash or fixup your commits, plugins won&amp;rsquo;t generate duplicate commits if no metadata has changed.&lt;/p&gt;

&lt;p&gt;What about security or external users trying to forge metadata? Since contributors cannot push directly to the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt; (like &lt;code&gt;main&lt;/code&gt;), they have to go through a branch and need to be merged. This means every single change to an issue&amp;rsquo;s status or metadata is subjected to a code review.&lt;/p&gt;

&lt;p&gt;If an external user tries to close someone else&amp;rsquo;s issue or manipulate IDs, it will stand out like a sore thumb in the git diff. Maintainers retain absolute control and can simply reject the rogue contribution. Security doesn&amp;rsquo;t rely on complex database permission tables, but on the battle-tested git review workflow you already know and trust.&lt;/p&gt;

&lt;p&gt;So, once you push, the server-side plugin does its job, and the next time you run &lt;code&gt;git pull&lt;/code&gt;, your local file will automatically look like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;id: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;user: email@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;status: OPEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# My title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt;9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;My description where I can detail what my problem is.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bingo! You get the exact same file, with the exact same metadata you wanted. Of course, a lot more needs to be added, like defining a &lt;code&gt;type: user&lt;/code&gt; to assign an issue, or perhaps a &lt;code&gt;type: status&lt;/code&gt; to define an enum of allowed values. But I think you see where I&amp;rsquo;m going with this feature!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: If you&amp;rsquo;re wondering about the barrier to entry for non-technical contributors, my long-term vision is to allow plugins to intercept standard HTTP requests. Tomorrow, a classic web form will be able to POST to GitRoot and automatically commit a &lt;code&gt;.md&lt;/code&gt; file under the hood. But for now, we are proudly dev-centric!)&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;so-what-about-the-label&quot;&gt;So, What About the Label?&lt;/h2&gt;

&lt;p&gt;Throughout all of this, you’ll notice that the term &amp;ldquo;issue&amp;rdquo; only appears within the plugins themselves. Nowhere have we talked about that &lt;code&gt;🐞 Issues&lt;/code&gt; button yet. We actually need to introduce another plugin before we can tackle that topic.&lt;/p&gt;

&lt;p&gt;First, we need a file that lists all of these &amp;ldquo;issues&amp;rdquo;. That’s exactly why the &lt;a href=&quot;plugins/name/silo.html&quot;&gt;silo plugin&lt;/a&gt; exists. It inspects all files and groups them whenever it finds a matching pattern.&lt;/p&gt;

&lt;p&gt;To list all open &amp;ldquo;issues&amp;rdquo;, we configure it like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: silo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;func&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: boards/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - append
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;callfunc&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;boards&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;description&lt;/span&gt;: All open issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;for&lt;/span&gt;: issues/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;format&lt;/span&gt;: table
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;paginator&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;50&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;selects&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;status: (.*)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;user: (.*)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;sort&lt;/span&gt;: select[0]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;sortOrder&lt;/span&gt;: desc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;tableHeader&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;| | status | user |&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;title&lt;/span&gt;: All issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;to&lt;/span&gt;: boards/issues.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;where&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;status: OPEN&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can read this configuration as: &amp;ldquo;For every file in the main branch, create a board called &amp;lsquo;All issues&amp;rsquo; containing links to all files matching &lt;code&gt;status: OPEN&lt;/code&gt;. Display them as a table with &lt;code&gt;status&lt;/code&gt; and &lt;code&gt;user&lt;/code&gt; columns.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After running &lt;code&gt;git commit &amp;amp;&amp;amp; git push &amp;amp;&amp;amp; git pull&lt;/code&gt;, you will get a new file at &lt;code&gt;boards/issues.md&lt;/code&gt; containing something like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# All issues
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;All open issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;|                                   | status | user            |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;| --------------------------------- | ------ | --------------- |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;| [&lt;span style=&quot;color:#ff79c6&quot;&gt;My title&lt;/span&gt;](&lt;span style=&quot;color:#50fa7b&quot;&gt;../issues/my_issue.md&lt;/span&gt;) | OPEN   | email@email.com |
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Silo has aggregated all your open issues into a single dashboard. Of course, you could easily create another board for closed issues in a separate file like &lt;code&gt;boards/closed.md&lt;/code&gt;, it&amp;rsquo;s entirely up to you.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: If you are sweating at the thought of a plugin scanning your entire repository on every single push, don&amp;rsquo;t panic. &lt;code&gt;silo&lt;/code&gt;, thanks to the plugin-sdk API, doesn&amp;rsquo;t do a full disk scan. It only processes the incremental diffs generated by the incoming commit. This is precisely why we restrict its scope to the &lt;code&gt;main&lt;/code&gt; branch in this example.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now that we have a page representing all our issues, we can finally render it in the browser. Because, as you might have guessed, the label causing all this debate lives in the Web UI.&lt;/p&gt;

&lt;h2 id=&quot;the-web-is-where-it-happens&quot;&gt;The Web is Where It Happens&lt;/h2&gt;

&lt;p&gt;To render my &lt;code&gt;boards/issues.md&lt;/code&gt; file on the web, I use the &lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex&lt;/a&gt; plugin, in conjunction with the &lt;a href=&quot;plugins/name/apex_markdown.html&quot;&gt;apex_markdown&lt;/a&gt; plugin, of course.&lt;/p&gt;

&lt;p&gt;These plugins take files from git and render them directly in the Web UI. I configure them like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: apex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;func&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - append
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - append
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;callfunc&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;pluginname&lt;/span&gt;: apex_markdown
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;funcname&lt;/span&gt;: renderMd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;menu&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 🏠 Home
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 📖 Documentation
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L38&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L38&quot;&gt;38&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /doc/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L39&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L39&quot;&gt;39&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 🔖 Versions
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L40&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L40&quot;&gt;40&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /CHANGELOG.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L41&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L41&quot;&gt;41&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: �� Plugins
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L42&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L42&quot;&gt;42&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /plugins/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L43&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L43&quot;&gt;43&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: �� Issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L44&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L44&quot;&gt;44&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /boards/issues.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L45&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L45&quot;&gt;45&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 🚀 Code
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L46&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L46&quot;&gt;46&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /worktree/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L47&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L47&quot;&gt;47&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: ☕ Blog
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L48&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L48&quot;&gt;48&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /blog/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L49&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L49&quot;&gt;49&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: �� Contact
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L50&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L50&quot;&gt;50&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /contact.html
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oh, do you see it now? Yes, right there: &lt;code&gt;display: 🐞 Issues&lt;/code&gt;. This is the exact configuration that tells Apex to add a global menu item with the &lt;code&gt;🐞 Issues&lt;/code&gt; label, pointing to the &lt;code&gt;/boards/issues.html&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;If you are wondering how Apex magically knows where to find your &amp;ldquo;issues&amp;rdquo;, the secret is: it doesn&amp;rsquo;t.&lt;/p&gt;

&lt;p&gt;Apex isn&amp;rsquo;t a smart router, it&amp;rsquo;s just a file translator. It blindly takes the Markdown structure generated by GitRoot and renders it into HTML. Because I know Apex transforms &lt;code&gt;boards/issues.md&lt;/code&gt; into &lt;code&gt;boards/issues.html&lt;/code&gt;, I simply hardcode that target URL in my menu configuration (&lt;code&gt;link: /boards/issues.html&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;And what about the links inside the table? Apex has a clever built-in rewrite engine. When it parses your generated board file, it automatically rewrites every internal Markdown link, turning &lt;code&gt;[My title](../issues/my_issue.md)&lt;/code&gt; into a standard web-friendly &lt;code&gt;&amp;lt;a href=&amp;quot;../issues/my_issue.html&amp;quot;&amp;gt;&lt;/code&gt;. No database, no dynamic routing, just plain old static files mapped one-to-one.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;If you change the label to &lt;code&gt;🐞 Tickets&lt;/code&gt; or &lt;code&gt;💡 Discussions&lt;/code&gt;, the &amp;ldquo;issue to rename issues into something else&amp;rdquo; can finally be closed. Ideally, you might also want to rename some directories in the other plugins&amp;rsquo; configurations, but it&amp;rsquo;s really not a big deal.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;m pretty sure you now understand why I didn&amp;rsquo;t immediately jump on resolving this task: everything we just saw is pure configuration, completely separated from the GitRoot core code.&lt;/p&gt;

&lt;p&gt;And to be perfectly honest, I haven&amp;rsquo;t changed it yet because none of the traditional options really appeal to me. I don&amp;rsquo;t particularly like &amp;ldquo;Tickets&amp;rdquo; or &amp;ldquo;Discussions&amp;rdquo;, maybe it should be &amp;ldquo;Feature Requests&amp;rdquo; instead (and yes, you can now model that in GitRoot too!).&lt;/p&gt;

&lt;p&gt;In fact, I personally call them &lt;code&gt;TODO&lt;/code&gt;s, because in reality, they are all just waiting for me to get them done.&lt;/p&gt;

&lt;h3 id=&quot;wait-why-stop-at-issues&quot;&gt;Wait&amp;hellip; Why stop at issues?&lt;/h3&gt;

&lt;p&gt;This is where the true power of GitRoot will blow your mind.&lt;/p&gt;

&lt;p&gt;Because GitRoot doesn&amp;rsquo;t care about databases, and because everything is just a file inside Git, the &lt;code&gt;silo&lt;/code&gt; plugin can aggregate anything. I don&amp;rsquo;t need a separate &lt;code&gt;TODO&lt;/code&gt; page for my tickets, because I already have one that automatically tracks the &lt;code&gt;//TODO&lt;/code&gt; comments inside my actual source code.&lt;/p&gt;

&lt;p&gt;Yes, you read that right. You can visit it live at &lt;a href=&quot;boards/todos.html&quot;&gt;https://gitroot.dev/boards/todos.html&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Every time I write a &lt;code&gt;//TODO&lt;/code&gt; in a &lt;code&gt;.go&lt;/code&gt;, &lt;code&gt;.rs&lt;/code&gt; or &lt;code&gt;.ts&lt;/code&gt; file, GitRoot parses it, aggregates it, and generates a dynamic dashboard. Tickets, documentation, source code comments&amp;hellip; it&amp;rsquo;s all the exact same data structure.&lt;/p&gt;

&lt;p&gt;To be clear, it doesn’t magically transform your source code comments into full-blown database-like issues with IDs and statuses. It simply extracts them to give you a centralized, living view of your technical debt, right alongside your official tickets. It’s the exact same data aggregation pipeline, tailored for two different use cases.&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to discover more plugins and configurations, feel free to browse through the &lt;a href=&quot;plugins&quot;&gt;🧩 Plugins section&lt;/a&gt;. However, for now, the best way to explore remains checking out the actual &lt;a href=&quot;.gitroot/plugins.yml&quot;&gt;GitRoot plugins.yml configuration file&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
</description><guid>https://gitroot.dev//blog/issues.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.4.0.md</link><title>0.4.0</title><pubDate>2026/04/22</pubDate><description>&lt;h1 id=&quot;the-teenager-starts-building-its-own-future&quot;&gt;The teenager starts building its own future&lt;/h1&gt;

&lt;h2 id=&quot;if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes&lt;/h2&gt;

&lt;p&gt;A lot has been done in this new release of GitRoot. When I saw other FOSS projects claiming &amp;ldquo;this is the biggest release&amp;rdquo;, I thought it was a joke. But in fact, it&amp;rsquo;s true: every new release gets bigger and bigger. It&amp;rsquo;s just astonishing to start a sprint with almost zero ideas and, 4 months later, see all of this accomplished.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you skim this article, don&amp;rsquo;t forget to check out the conclusion about &lt;a href=&quot;blog/version-0.4.0.html#what-next&quot;&gt;&amp;ldquo;what&amp;rsquo;s next?&amp;rdquo;&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;the-thing-i-m-most-proud-of&quot;&gt;The thing i&amp;rsquo;m most proud of&lt;/h3&gt;

&lt;p&gt;Executors can finally build GitRoot! As I love to say: GitRoot can build GitRoot on GitRoot. And it was a pain to do. &lt;a href=&quot;blog/version-0.4.0.html#executor&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-thing-with-the-most-visibility&quot;&gt;The thing with the most visibility&lt;/h3&gt;

&lt;p&gt;The plugin registry is here! After a lot of reading and reflecting, I started this &amp;ldquo;epic story&amp;rdquo;. Today is only the beginning, but you can now find plugins directly at &lt;a href=&quot;plugins/index.html&quot;&gt;gitroot.dev/plugins&lt;/a&gt;. What&amp;rsquo;s next? &lt;a href=&quot;blog/version-0.4.0.html#plugin-registry&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-thing-that-changes-everything-but-was-so-simple&quot;&gt;The thing that changes everything but was so simple&lt;/h3&gt;

&lt;p&gt;Previously, a branch (and graft) couldn&amp;rsquo;t be rendered on the web. The problem was: if I have an &lt;code&gt;index.md&lt;/code&gt; in my git that renders at &lt;code&gt;myinstance.com/index.html&lt;/code&gt;, anyone can create a branch. If a &amp;ldquo;bad user&amp;rdquo; comes along, changes everything in this file, creates a branch, and pushes for review, where should this file render? Certainly not at &lt;code&gt;myinstance.com/index.html&lt;/code&gt;. &lt;a href=&quot;blog/version-0.4.0.html#apex-render-branch&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-thing-that-was-more-complex-than-i-thought&quot;&gt;The thing that was more complex than I thought&lt;/h3&gt;

&lt;p&gt;Apex was a big plugin. Basically, it manages the rendering of your git files to the web. But over time, it started to grow out of control because of markdown, code, worktrees, emojis&amp;hellip; A simple (in theory) solution was to split it into multiple plugins. &lt;a href=&quot;blog/version-0.4.0.html#plugin-deps&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-things-i-m-just-happy-are-done&quot;&gt;The things I&amp;rsquo;m just happy are done&lt;/h3&gt;

&lt;p&gt;No more mandatory signed commits! Well, it&amp;rsquo;s still better if you sign them, but this change means the &lt;code&gt;.gitroot/init.sh&lt;/code&gt; has been &lt;del&gt;deleted&lt;/del&gt; moved to a plugin. &lt;a href=&quot;blog/version-0.4.0.html#sigma-plugin&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Moving a file from git to web was hard, deleting a file was &amp;ldquo;not implemented&amp;rdquo;, and a lot of APIs were missing in the plugin-sdk. I got my hands dirty with all that, and now everything is running smoothly. &lt;a href=&quot;blog/version-0.4.0.html#fs-plugin-sdk&quot;&gt;Read more&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;in-depth-details&quot;&gt;In-depth Details&lt;/h2&gt;

&lt;h3 id=&quot;executor&quot;&gt;Executor&lt;/h3&gt;

&lt;p&gt;When I started this feature, I just wanted a lightweight container to run &lt;code&gt;make build&lt;/code&gt; and output the result as a &lt;code&gt;gitroot-next&lt;/code&gt; canary binary. If I had known how much time and energy it would actually cost, I’m not sure I would have taken this road.&lt;/p&gt;

&lt;p&gt;At first, I chose &lt;a href=&quot;doc/technicals/executors.html#bwrap-executor&quot;&gt;bubblewrap&lt;/a&gt; as a lightweight container, and immediately started building a &lt;a href=&quot;doc/technicals/executors.html#container-executor&quot;&gt;container&lt;/a&gt; version. Then I realized I needed a &lt;a href=&quot;doc/technicals/executors.html#baremetal-executor&quot;&gt;bareMetal&lt;/a&gt; one. And then, I needed a way to run it on other machines, so &lt;a href=&quot;doc/technicals/executors.html#ssh-executor&quot;&gt;ssh&lt;/a&gt; came along.&lt;/p&gt;

&lt;p&gt;But when I launched it for the first time, I discovered that having &lt;code&gt;make&lt;/code&gt; isn&amp;rsquo;t enough for GitRoot, I need Golang, Rust, TinyGo, and so on. &amp;ldquo;No problem,&amp;rdquo; I thought, &amp;ldquo;I&amp;rsquo;ll use &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt; to manage that.&amp;rdquo; But running &lt;code&gt;mise install&lt;/code&gt; takes a lot of time (like 10-15 minutes on my computer). So, I built a cache system to ensure that hit only happens the first time.&lt;/p&gt;

&lt;p&gt;Still not the end! After that, I needed to configure port numbers, so I built a complete environment variable system, and used it to configure some &lt;code&gt;mise&lt;/code&gt; paths.&lt;/p&gt;

&lt;p&gt;Is it finished? Not yet! To be able to move a binary generated from a bwrap environment to the web, it has to travel a lot. And by &amp;ldquo;a lot,&amp;rdquo; I mean moving it to a place where the plugin that launched the executor can access it: its own cache. Then the plugin needs to move it from there to the web. No problem, I just added a bunch of &lt;a href=&quot;blog/modifying-API-SDK-plugin.html&quot;&gt;SDK API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After all that, some commands finally started working&amp;hellip; well, kind of. When I ran a bwrap or a container command, I couldn&amp;rsquo;t get the stats of what was happening inside. Having process stats is something I’m proud of (even if it’s still evolving in future versions), but seeing that bwrap reported 20 MB of RAM while the sub-process was actually consuming 80 MB was very frustrating. Furthermore, I needed a way to extract stats from the remote SSH executor.&lt;/p&gt;

&lt;p&gt;I tried a lot of things, but ultimately, I chose to build an &lt;a href=&quot;app/executor/main.go&quot;&gt;executor CLI&lt;/a&gt; to handle it. I now inject that binary inside your bwrap, container, or SSH environment. This little CLI launches commands, gathers system stats, command status, and logs. It packs everything into a well-formatted JSON and returns it to the executor in GitRoot. GitRoot then manages artifacts/caches and hands the result back to the calling plugin (probably &lt;a href=&quot;plugins/name/hop.html&quot;&gt;hop&lt;/a&gt;), which tells you what’s happening. In this case, it reports back so the &lt;a href=&quot;plugins/name/grafter.html&quot;&gt;grafter plugin&lt;/a&gt; can include the info in the current graft.&lt;/p&gt;
&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;295.9538&quot; height=&quot;429.0792&quot; viewBox=&quot;0 0 295.9538 429.0792&quot;&gt;&lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;295.9538&quot; height=&quot;429.0792&quot; fill=&quot;transparent&quot;/&gt;&lt;defs&gt;&lt;marker id=&quot;arrow-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 10 5 L 0 10 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-start-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;4.5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 5 L 10 10 L 10 0 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;path id=&quot;edge-0&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-0&quot; d=&quot;M 210.589,59.000 L 210.589,107.236&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot; x=&quot;105.97&quot; y=&quot;62.00&quot; width=&quot;90.62&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;151.28&quot; y=&quot;79.70&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;151.28&quot; dy=&quot;0.00&quot;&gt;Exec cmds&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-1&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-1&quot; d=&quot;M 210.679,158.236 L 210.679,206.472&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;path id=&quot;edge-2&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-2&quot; d=&quot;M 210.835,257.472 L 210.835,305.708&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-2&quot; data-label-kind=&quot;center&quot; x=&quot;17.60&quot; y=&quot;260.47&quot; width=&quot;179.24&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-2&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;107.22&quot; y=&quot;278.17&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;107.22&quot; dy=&quot;0.00&quot;&gt;bwrap/container/ssh&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-3&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-3&quot; d=&quot;M 210.955,356.708 L 210.955,370.079&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;path id=&quot;edge-4&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-4&quot; d=&quot;M 166.834,395.579 L 153.002,395.579 L 120.042,347.208 L 133.874,347.208&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;path id=&quot;edge-5&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-5&quot; d=&quot;M 133.874,315.208 L 8.000,315.208 L 8.000,247.972 L 165.077,247.972&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-5&quot; data-label-kind=&quot;center&quot; x=&quot;46.42&quot; y=&quot;258.31&quot; width=&quot;206.12&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-5&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;149.48&quot; y=&quot;276.01&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;149.48&quot; dy=&quot;0.00&quot;&gt;status/code/logs/stats&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-6&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-6&quot; d=&quot;M 165.077,215.972 L 41.355,215.972 L 41.355,148.736 L 153.161,148.736&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-6&quot; data-label-kind=&quot;center&quot; x=&quot;56.06&quot; y=&quot;159.07&quot; width=&quot;206.12&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-6&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;159.12&quot; y=&quot;176.77&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;159.12&quot; dy=&quot;0.00&quot;&gt;status/code/logs/stats&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-7&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-7&quot; d=&quot;M 153.161,116.736 L 26.706,116.736 L 26.706,33.500 L 167.201,33.500&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-7&quot; data-label-kind=&quot;center&quot; x=&quot;57.12&quot; y=&quot;59.83&quot; width=&quot;206.12&quot; height=&quot;28.40&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.00&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.00&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-7&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;160.18&quot; y=&quot;77.53&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;160.18&quot; dy=&quot;0.00&quot;&gt;status/code/logs/stats&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;rect x=&quot;167.20&quot; y=&quot;8.00&quot; width=&quot;86.75&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.58&quot; y=&quot;37.00&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.58&quot; dy=&quot;0.00&quot;&gt;Hop&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;153.16&quot; y=&quot;107.24&quot; width=&quot;114.88&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.60&quot; y=&quot;136.24&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.60&quot; dy=&quot;0.00&quot;&gt;GitRoot&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;133.87&quot; y=&quot;305.71&quot; width=&quot;154.08&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.91&quot; y=&quot;334.71&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.91&quot; dy=&quot;0.00&quot;&gt;executor cli&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;166.83&quot; y=&quot;370.08&quot; width=&quot;88.32&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;211.00&quot; y=&quot;399.08&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;211.00&quot; dy=&quot;0.00&quot;&gt;cmd&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;165.08&quot; y=&quot;206.47&quot; width=&quot;91.36&quot; height=&quot;51.00&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;text x=&quot;210.76&quot; y=&quot;235.47&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;210.76&quot; dy=&quot;0.00&quot;&gt;exec&lt;/tspan&gt;&lt;/text&gt;&lt;g transform=&quot;translate(210.59 107.24) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(210.68 206.47) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(210.84 305.71) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(210.95 370.08) rotate(90.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(133.87 347.21) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(165.08 247.97) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(153.16 148.74) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;g transform=&quot;translate(167.20 33.50) rotate(0.00)&quot;&gt;&lt;polygon points=&quot;0,0 -10.40,6.24 -10.40,-6.24&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;2&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;/g&gt;&lt;/svg&gt;
&lt;p&gt;Phew, is that enough now? It should be, but we&amp;rsquo;ll see how it performs in real-time on &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev&quot;&gt;gitroot.dev&lt;/a&gt;. Not everything is finished: for example, the SSH executor doesn’t manage cache yet &lt;a href=&quot;issues/94af-executor-cache.html&quot;&gt;#94af&lt;/a&gt; and can&amp;rsquo;t use the container strategy &lt;a href=&quot;issues/f050-ssh-executor-subCmd.html&quot;&gt;#f050&lt;/a&gt;. You can’t run the executor on other architectures &lt;a href=&quot;issues/4d49-executor-compile-other-archs.html&quot;&gt;#4d49&lt;/a&gt; yet, and I’m sure you’ll soon want a way to clean up temp directories &lt;a href=&quot;issues/647f-clean-executor-temp-dir.html&quot;&gt;#647f&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But hey, let’s celebrate what we’ve built before jumping into the next sprint!&lt;/p&gt;

&lt;h3 id=&quot;plugin-registry&quot;&gt;Plugin registry&lt;/h3&gt;

&lt;p&gt;Until now, explaining GitRoot was always about &amp;ldquo;data in git and the plugin system&amp;rdquo;, but what exactly are the plugins? Where are they? What can they do? All of that was a bit mysterious. With this milestone, I’m answering some of those questions with a first shot at a registry. Be careful, though: this first version is just a &amp;ldquo;hand-written&amp;rdquo; preview of what it could be in the future. If you look at the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/plugins/index.md&quot;&gt;source&lt;/a&gt;, you’ll see that everything is ready to be automated, but nothing is&amp;hellip; yet.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;m not entirely sure what you, as a consumer, want to see here. Probably an explanation of what the plugin does, some docs, and the available versions. But as a plugin creator, you likely want to manage all of that in your own repository instead of the &lt;a href=&quot;plugins/index.html&quot;&gt;gitroot.dev registry&lt;/a&gt;. I chose to keep the registry very light and put the details directly in the &lt;a href=&quot;app/plugins/apex/index.html&quot;&gt;plugin&amp;rsquo;s directory&lt;/a&gt; to give you an idea of what your own repo could look like.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ll need to iterate on this. Even if I choose to embed package details in the registry, I still need to decide how: maybe via a JSON in your repo, maybe in the registry itself, or perhaps inside your package (WASM)? I don&amp;rsquo;t know yet what’s simplest for you and for the registry.&lt;/p&gt;

&lt;p&gt;Anyway, I hope this registry helps you understand what a plugin is and how it shapes your forge. Some are small (&lt;a href=&quot;plugins/name/ladybug.html&quot;&gt;ladybug&lt;/a&gt;), others are massive (&lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex&lt;/a&gt;), and some aren&amp;rsquo;t usable solo as they are &lt;a href=&quot;blog/version-0.4.0.html#plugin-deps&quot;&gt;dependencies&lt;/a&gt; for others (like &lt;a href=&quot;plugins/name/apex_mermaid.html&quot;&gt;apx_mermaid&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Since plugins are now decoupled from GitRoot, I’ve moved all changelogs from the &lt;a href=&quot;CHANGELOG.html&quot;&gt;general&lt;/a&gt; one to the individual plugin directories. This way, I’ll be able to release plugin versions independently of GitRoot (and vice versa). I hope to break the plugin API less often, even if I know I probably will in the near future. All of this paves the way for you to build your own plugins and I hope to see them in the gitroot.dev registry soon. Or, of course, you can just build your own registry!&lt;/p&gt;

&lt;h3 id=&quot;apex-render-branch&quot;&gt;Apex render branch&lt;/h3&gt;

&lt;p&gt;For as long as I can remember, this has been a problem with GitRoot: how to render arbitrary changes, especially deletions, on the web? I had imagined a lot of strategies. The last one was to render inside a directory named after the git tree ID (the abstract representation of a working directory). Then, I’d create a symlink between that tree and a main directory.&lt;/p&gt;

&lt;p&gt;The idea was: if you push a file in a branch, git gives you a tree ID like &lt;code&gt;a654fs&lt;/code&gt;. It would be represented as &lt;code&gt;mydomain.com/myBranch/file -&amp;gt; mydomain.com/tree/a654fs/file&lt;/code&gt;. When you merged that branch, GitRoot would simply symlink the main directory to it: &lt;code&gt;mydomain.com/file -&amp;gt; mydomain.com/tree/a654fs/file&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Elegant, but honestly, a total over-engineered mess. When I actually sat down to code this, I ended up adding just two lines to the &lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex plugin&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; p.currentCommit.Branch &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; p.currentCommit.Branch &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; p.config.defaultBranch {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	path = filepath.&lt;span style=&quot;color:#50fa7b&quot;&gt;Join&lt;/span&gt;(p.config.branchesDir, p.currentCommit.Branch, path)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And&amp;hellip; yeah, that was enough. I&amp;rsquo;ve been overthinking this since the very beginning of GitRoot! Rendering files from branches into a dedicated branches directory&amp;hellip; It&amp;rsquo;s simple. Just like GitRoot should be.&lt;/p&gt;

&lt;p&gt;So, what does it mean in practice? The file is now rendered at &lt;code&gt;myinstance.com/mybranch/index.html&lt;/code&gt; and the graft at &lt;code&gt;myinstance.com/mybranch/grafts/mybranch.html&lt;/code&gt;. How did I not think of something this simple before?&lt;/p&gt;

&lt;h3 id=&quot;plugin-deps&quot;&gt;Plugin deps&lt;/h3&gt;

&lt;p&gt;The catalyst for this development was the &lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex plugin&lt;/a&gt;. At over 3 MB, it was by far the largest plugin. It was packed with configuration, specifically a bunch of booleans to &lt;a href=&quot;issues/8b29-apex-working-tree.html&quot;&gt;toggle features&lt;/a&gt;. Furthermore, I needed to render Markdown in other plugins, like &lt;a href=&quot;plugins/name/pollen.html&quot;&gt;pollen&lt;/a&gt;, to create a proper &lt;a href=&quot;issues/83d8-pollen-content-flux.html&quot;&gt;RSS feed&lt;/a&gt;. It became clear to me: this plugin needed to be split into smaller, more specialized ones.&lt;/p&gt;

&lt;p&gt;So, I started building the &lt;a href=&quot;plugins/name/apex_markdown.html&quot;&gt;apex_markdown plugin&lt;/a&gt;, then &lt;a href=&quot;plugins/name/apex_code.html&quot;&gt;apex_code&lt;/a&gt;, and next week &lt;a href=&quot;plugins/name/apex_mermaid.html&quot;&gt;apex_mermaid&lt;/a&gt;. But what I hadn&amp;rsquo;t fully anticipated was the sheer amount of work required to make it all work together.&lt;/p&gt;

&lt;p&gt;What if a dependency is missing? What if a method is deprecated or removed in a newer version? To handle this, I’ve chosen to make all dependencies entirely optional for now. It’s an easy choice to make today, but I might change my mind tomorrow, after all, there’s a reason most package managers handle version ranges and resolution.&lt;/p&gt;

&lt;p&gt;This new architecture allows for adding tons of features without rebuilding an entire plugin. Imagine adding Org-mode or RST support: you just swap out &lt;a href=&quot;plugins/name/apex_markdown.html&quot;&gt;apex_mardown&lt;/a&gt; instead of touching the whole stack. It also allows other plugins to reuse existing behaviors effortlessly.&lt;/p&gt;

&lt;p&gt;With this in place, I’m not even sure if I’ll keep the &lt;code&gt;report&lt;/code&gt; system, which was an other way of passing information between plugins.&lt;/p&gt;

&lt;h3 id=&quot;sigma-plugin&quot;&gt;Sigma plugin&lt;/h3&gt;

&lt;p&gt;Signed commits should be the default in git today. With SSH signing, it’s easy to do, and everyone should use it because impersonating a name and email is way too easy in git. When I started GitRoot, I made signed commits mandatory before allowing a push. But in fact, making signatures mandatory should be a choice for the instance owner. GitRoot is all about the freedom to choose how your forge works for you.&lt;/p&gt;

&lt;p&gt;Furthermore, a friend told me he preferred signing with PGP and already had his setup ready. So why force him to use a &lt;code&gt;.gitroot/init.sh&lt;/code&gt;? It’s frustrating when you want to test a new forge and you’re forced to run random sh commands, even if it&amp;rsquo;s just to configure your git client.&lt;/p&gt;

&lt;p&gt;With this release, I have completely removed &lt;code&gt;init.sh&lt;/code&gt; from GitRoot itself. But of course, as always, I’ve created a plugin to &amp;ldquo;almost&amp;rdquo; recreate this feature. The only difference is: instead of refusing your commit when you push, the &lt;a href=&quot;plugins/name/stigma.html&quot;&gt;stigma plugin&lt;/a&gt; will simply report unsigned or badly signed commits in your graft.&lt;/p&gt;

&lt;p&gt;Now, you have the choice to accept unsigned commits in your repo. You can even ignore the whole signing topic entirely if you don&amp;rsquo;t install the stigma plugin.&lt;/p&gt;

&lt;p&gt;That said, I’m still convinced that signing commits should remain the default.&lt;/p&gt;

&lt;h3 id=&quot;fs-plugin-sdk&quot;&gt;FS plugin-sdk&lt;/h3&gt;

&lt;p&gt;After finishing all the &lt;a href=&quot;blog/version-0.4.0.html#executor&quot;&gt;executors features&lt;/a&gt;, one thing was still missing: the ability for a plugin to move a file from one path to another. As explained in this &lt;a href=&quot;blog/modifying-API-SDK-plugin.html&quot;&gt;blog post&lt;/a&gt;, even though a plugin sees one single filesystem, GitRoot actually uses three different ones: one for cache (a directory), one for the web (a subdirectory), and a virtual one for the git working directory.&lt;/p&gt;

&lt;p&gt;I was missing several methods in both the Rust and TS SDKs, and the existing API was poorly designed with inconsistent naming. I decided to redesign everything and added a lot of new methods accessible via &lt;code&gt;FS&lt;/code&gt; in the SDK. Now, you can copy, move, delete, replace, and write files in every filesystem. Moving and copying can now be done between different filesystems, making it easy to make a file accessible on the web even if it originated from git or the cache.&lt;/p&gt;

&lt;p&gt;I have marked all old methods as deprecated, and they will be removed in the next version of GitRoot (or more specifically, the plugin-sdk, though they share the same release number for now).&lt;/p&gt;

&lt;h2 id=&quot;what-next&quot;&gt;What next?&lt;/h2&gt;

&lt;p&gt;I have a massive roadmap for GitRoot (in no particular order):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discover GitRoot without installing anything.&lt;/li&gt;
&lt;li&gt;Create your own plugins with ease.&lt;/li&gt;
&lt;li&gt;Transform GitRoot into a registry for (at least) Go, Rust, and NPM.&lt;/li&gt;
&lt;li&gt;Plugins should be usable via PURL.&lt;/li&gt;
&lt;li&gt;Plugins can send/receive HTTP requests.&lt;/li&gt;
&lt;li&gt;Plugins can be scheduled.&lt;/li&gt;
&lt;li&gt;Rebase go-git to take advantage of their performance improvements.&lt;/li&gt;
&lt;li&gt;Generate the plugin SDK automatically.&lt;/li&gt;
&lt;li&gt;Add plugin SDKs for (at least) Zig, Kotlin, and Python.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yeah, I could go on all night. And those are just core GitRoot features. I haven&amp;rsquo;t even mentioned making instances to build projects in hosted environments. I haven&amp;rsquo;t talked about specific plugins (sending a &amp;ldquo;toot&amp;rdquo; on commit, managing secrets with age, translating your app&amp;hellip;). I haven&amp;rsquo;t even touched on federating GitRoot with other GitRoot instances, or with Forgejo, SourceHut, or Radicle.&lt;/p&gt;

&lt;p&gt;All of that is possible, and I’m confident it will be accessible in the future. But &amp;ldquo;when&amp;rdquo;? That’s the real question. If you think you can help in any way (coding, of course, but also management, communication, or just to brainstorm), please &lt;a href=&quot;contact.html&quot;&gt;let me know&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is only the beginning. GitRoot will get better and better with every release.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.4.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.3.0.md</link><title>0.3.0</title><pubDate>2025/12/20</pubDate><description>&lt;h1 id=&quot;from-baby-to-teenager-gitroot-become-a-forge&quot;&gt;From &amp;ldquo;baby to teenager&amp;rdquo; GitRoot become a forge&lt;/h1&gt;

&lt;h2 id=&quot;if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes&lt;/h2&gt;

&lt;p&gt;Previously, GitRoot served as a &lt;strong&gt;Source Code Management (SCM)&lt;/strong&gt; system, allowing you to create Git repositories and manage access permissions. However, GitRoot was never intended to be &lt;em&gt;just&lt;/em&gt; an SCM. With this &lt;strong&gt;v0.3.0&lt;/strong&gt; release, it evolves into a full-fledged &lt;strong&gt;forge&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To activate this new capability on your instance, &lt;a href=&quot;blog/version-0.3.0.html#for-admin-activating-an-executor&quot;&gt;follow the administrator section&lt;/a&gt;. To start using it as a user, &lt;a href=&quot;blog/version-0.3.0.html#for-user-using-the-hop-plugin&quot;&gt;see the user section&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The introduction of &lt;strong&gt;executors&lt;/strong&gt; brings a crucial new feature: the &lt;a href=&quot;blog/version-0.3.0.html#plugin-api-report-and-reporter&quot;&gt;report and reporter plugin API&lt;/a&gt;. This API allows any plugin to provide informational feedback to the user. &lt;strong&gt;Important:&lt;/strong&gt; avoid reporting low-level technical details here (use the &lt;code&gt;log&lt;/code&gt; and &lt;code&gt;logError&lt;/code&gt; APIs for that). The initial implementation uses this feature to display command statistics (CPU, memory, etc.) from the new &lt;code&gt;hop&lt;/code&gt; plugin in a clean table via the &lt;code&gt;grafter&lt;/code&gt; plugin.&lt;/p&gt;

&lt;p&gt;With the new &lt;code&gt;hop&lt;/code&gt; plugin, GitRoot now supports 3 languages (Go, TypeScript, Rust) across 4 compilers (Go, TinyGo, Rust, AssemblyScript). Setting up a development environment for this polyglot stack is complex, and keeping it updated is even harder, unless you have the right tools. For this iteration, I have selected &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt; as the default tool for &lt;a href=&quot;blog/version-0.3.0.html#gitroot-bets-on-mise&quot;&gt;managing the GitRoot dev environment&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Beyond technical features, a new focus has been placed on &lt;a href=&quot;blog/version-0.3.0.html#communication-and-community-building&quot;&gt;communication&lt;/a&gt;. You can now follow daily progress on our new fediverse instance (@forge@gitroot.dev), engage in direct chat on matrix (#gitroot:matrix.org), and read a new blog post detailing concrete ways to contribute today.&lt;/p&gt;

&lt;p&gt;Finally, I discuss &lt;a href=&quot;blog/version-0.3.0.html#apex-a-better-web-experience&quot;&gt;Apex&lt;/a&gt; and many &lt;a href=&quot;blog/version-0.3.0.html#and-others-useful-updates&quot;&gt;other tiny but useful things&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;in-depth-details&quot;&gt;In-depth Details&lt;/h2&gt;

&lt;h3 id=&quot;for-admin-activating-an-executor&quot;&gt;For admin: activating an executor&lt;/h3&gt;

&lt;p&gt;When you update GitRoot to &lt;code&gt;0.3.0&lt;/code&gt; a new configuration block will appear in the &lt;code&gt;.gitroot/forgeConfig.yml&lt;/code&gt; file of your &lt;code&gt;root&lt;/code&gt; repository:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;execconf&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;baremetal&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;bwrap&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;user&lt;/span&gt;: nobody
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;uid&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;65534&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;gid&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;65534&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;robind&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;bind&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;container&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;bin&lt;/span&gt;: podman
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;ssh&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;enabled&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;hosts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;user&lt;/span&gt;: myUSer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;address&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;127.0.0.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;port&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;22&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;publickey&lt;/span&gt;: ssh-ed25519 AAAAC3NzaC1...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can enable multiple executors, but GitRoot will only use the first one enabled, checking them in this order: &lt;code&gt;bareMetal&lt;/code&gt;, &lt;code&gt;bwrap&lt;/code&gt;, &lt;code&gt;container&lt;/code&gt; and then &lt;code&gt;ssh&lt;/code&gt;. If you activate an executor, remember to also add the &lt;code&gt;hop&lt;/code&gt; plugin, as it is the only known plugin designed to utilize them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;bare-metal-simplest-but-most-dangerous&quot;&gt;Bare Metal: simplest but most dangerous&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;bareMetal&lt;/code&gt; executor runs commands directly on the host machine. It requires no external dependencies and should work on any hardware. Be extremely cautious: users of your GitRoot instance will gain the ability to execute arbitrary code on the host.&lt;/p&gt;

&lt;h4 id=&quot;bubblewrap-bwrap-lightweight-sandboxing&quot;&gt;Bubblewrap (bwrap): lightweight sandboxing&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;bwrap&lt;/code&gt; executor introduces a lightweight container (sandbox) between executed commands and the host, similar to the technology used by &lt;a target=&quot;_blank&quot; href=&quot;https://flatpak.org/&quot;&gt;flatpack&lt;/a&gt;. To use it, you must install the &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/containers/bubblewrap&quot;&gt;bubblewrap&lt;/a&gt; application.&lt;/p&gt;

&lt;p&gt;By default, GitRoot configures &lt;code&gt;bwrap&lt;/code&gt; to use the &lt;code&gt;nobody&lt;/code&gt; user for restriction, though this can be changed. You can also define bindings between your host and the commands running inside the container. For example, &lt;code&gt;robind: [&amp;quot;/var/log/caddy/access.log&amp;quot;]&lt;/code&gt; would allow a job to execute &lt;code&gt;cat /var/log/caddy/access.log&lt;/code&gt;.&lt;/p&gt;

&lt;h4 id=&quot;container-heavyweight-but-most-flexible&quot;&gt;Container: heavyweight but most flexible&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;container&lt;/code&gt; executor enables the execution of commands within &lt;a target=&quot;_blank&quot; href=&quot;https://podman.io/&quot;&gt;podman&lt;/a&gt; or &lt;a target=&quot;_blank&quot; href=&quot;https://www.docker.com/&quot;&gt;docker&lt;/a&gt; containers, allowing your users to run any OCI image. The only requirement for the administrator is to install either &lt;a target=&quot;_blank&quot; href=&quot;https://podman.io/docs/installation&quot;&gt;podman&lt;/a&gt; or &lt;a target=&quot;_blank&quot; href=&quot;https://www.docker.com/&quot;&gt;docker&lt;/a&gt;. Remember to change the &lt;code&gt;bin&lt;/code&gt; property to &lt;code&gt;docker&lt;/code&gt; if that is your chosen runtime.&lt;/p&gt;

&lt;p&gt;GitRoot includes a useful feature for containers: if the user provides a path to a &lt;code&gt;Containerfile&lt;/code&gt; or &lt;code&gt;Dockerfile&lt;/code&gt;, GitRoot will automatically build the image before running commands inside it.&lt;/p&gt;

&lt;h4 id=&quot;ssh-remote-execution-alpha&quot;&gt;SSH: remote execution (alpha)&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;ssh&lt;/code&gt; executor allows commands to be executed on a remote host via a standard SSH connection. You simply provide the standard SSH connection details. This executor should be considered the most alpha of all options (and yes, all others are also considered alpha).&lt;/p&gt;

&lt;h3 id=&quot;for-user-using-the-hop-plugin&quot;&gt;For user: using the &lt;code&gt;hop&lt;/code&gt; plugin&lt;/h3&gt;

&lt;p&gt;To execute a build (or command execution), users must activate the &lt;code&gt;hop&lt;/code&gt; plugin and configure its execution rules:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;url&lt;/span&gt;: /home/rmaneschi/projects/gitroot/app/testsuite/../plugins/hop/hop-0.0.1.wasm
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: hop
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;version&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;0.0.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;active&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          - &lt;span style=&quot;color:#ff79c6&quot;&gt;command&lt;/span&gt;: cat
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          &lt;span style=&quot;color:#ff79c6&quot;&gt;build&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          &lt;span style=&quot;color:#ff79c6&quot;&gt;cmds&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            - &lt;span style=&quot;color:#ff79c6&quot;&gt;args&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;                - README.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;              &lt;span style=&quot;color:#ff79c6&quot;&gt;cmd&lt;/span&gt;: cat
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;          &lt;span style=&quot;color:#ff79c6&quot;&gt;env&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add all desired commands for the plugin to call in &lt;code&gt;run.write.exec&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then in &lt;code&gt;run.configuration.exec&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;set environment variables via &lt;code&gt;env&lt;/code&gt; (e.g., &lt;code&gt;&amp;quot;MY_ENV=my_val&amp;quot;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;specify the &lt;code&gt;build&lt;/code&gt; property (only for the &lt;code&gt;container&lt;/code&gt; executor) to use an existing image (e.g., &lt;code&gt;docker.io/bash:5.3.3&lt;/code&gt;) or to build one from a file (e.g., &lt;code&gt;/my/path/to/Containerfile&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;define the commands and arguments to execute under &lt;code&gt;cmds&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;plugin-api-report-and-reporter&quot;&gt;Plugin api report and reporter&lt;/h3&gt;

&lt;p&gt;Occasionally, plugins need to communicate important information to the user. For instance, the &lt;code&gt;ladybug&lt;/code&gt; plugin might need to report invalid metadata. In traditional forges, a web interface serves this purpose, but GitRoot operates without a fixed UI.&lt;/p&gt;

&lt;p&gt;Past considerations included sending emails (a solution often disliked by developers) or leveraging external communication methods like RSS via the &lt;code&gt;pollen&lt;/code&gt; plugin (introduced in v0.2.0) or the Fediverse via a Go-to-Social instance. As always, GitRoot aims to provide options rather than dictate a single solution.&lt;/p&gt;

&lt;p&gt;To allow reporting without forcing a communication channel, I&amp;rsquo;ve created two new plugin APIs.&lt;/p&gt;

&lt;p&gt;Report: plugins call this API with a severity level (&lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, or &lt;code&gt;error&lt;/code&gt;) and an array of strings to display. GitRoot stacks all reports for every commit and plugin.&lt;/p&gt;

&lt;p&gt;Reporter API: after reports are collected, GitRoot calls all plugins that implement the &lt;code&gt;Reporter&lt;/code&gt; interface.&lt;/p&gt;

&lt;p&gt;Currently, the &lt;code&gt;grafter&lt;/code&gt; plugin is the only known reporter; it adds the collected reports to the graft output. This two new API opens up a wide range of possibilities for future versions. I have several ideas, but I encourage you to &lt;a href=&quot;contact.html&quot;&gt;share yours&lt;/a&gt;!&lt;/p&gt;

&lt;h3 id=&quot;gitroot-bets-on-mise&quot;&gt;GitRoot bets on mise&lt;/h3&gt;

&lt;p&gt;Managing a diverse development environment that spans Go, TypeScript, and Rust can be a significant hurdle. In this iteration, I have standardized on mise as the official tool for managing the GitRoot dev environment. This choice simplifies installation and ensures all dependencies remain correctly updated for development.&lt;/p&gt;

&lt;p&gt;This choice seems to be a good one, as we welcome a new contributor who has already integrated some common tasks using &lt;code&gt;mise&lt;/code&gt;. Thanks to their efforts, we can now simply run &lt;code&gt;mise run build&lt;/code&gt; to compile GitRoot or &lt;code&gt;mise run test&lt;/code&gt; to execute all tests.&lt;/p&gt;

&lt;h3 id=&quot;communication-and-community-building&quot;&gt;Communication and community building&lt;/h3&gt;

&lt;p&gt;Alongside the technical work for v0.3.0, a focused effort was put into non-technical tasks to improve communication with the community.&lt;/p&gt;

&lt;p&gt;To allow you to follow daily progress and current thoughts, I have set up a &lt;a target=&quot;_blank&quot; href=&quot;https://gotosocial.org/&quot;&gt;GoToSocial&lt;/a&gt; (Mastodon) instance. You can find the official account at &lt;code&gt;@forge@gitroot.dev&lt;/code&gt; or directly at &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev&quot;&gt;https://gts.gitroot.dev&lt;/a&gt;. This is the best place for real-time updates.&lt;/p&gt;

&lt;p&gt;I also established a direct chat room on &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.org/&quot;&gt;matrix&lt;/a&gt;, accessible via &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/#gitroot:matrix.org&quot;&gt;https://matrix.to/#/#gitroot:matrix.org&lt;/a&gt;. Feel free to stop by to ask questions or just say hello.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ve added a &lt;a href=&quot;contact.html&quot;&gt;contact&lt;/a&gt; page to ensure you can easily find a way to reach me. It includes multiple options such as email, mastodon, or matrix for direct chat. Please don&amp;rsquo;t hesitate to come say &amp;ldquo;hi&amp;rdquo;!&lt;/p&gt;

&lt;p&gt;Finally, I published a blog post titled &lt;a href=&quot;blog/how-to-help_en_11-2025.html&quot;&gt;&amp;ldquo;How to help building GitRoot 11-2025&amp;rdquo;&lt;/a&gt;. The purpose of this post is to present you with concrete, immediate contribution opportunities, rather than speculating on future possibilities. If you wish to get involved, this is the place to start.&lt;/p&gt;

&lt;h3 id=&quot;apex-a-better-web-experience&quot;&gt;Apex a better web experience&lt;/h3&gt;

&lt;p&gt;The Apex plugin receives numerous bug fixes and quality of life improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;favicon support: a favicon can now be added via the configuration&lt;/li&gt;
&lt;li&gt;styled 404 page: the 404 error page now correctly uses the site&amp;rsquo;s CSS&lt;/li&gt;
&lt;li&gt;HTML metadata configuration: I have added configuration options for HTML metadata, which is useful for SEO and for including specific tags like &lt;code&gt;og&lt;/code&gt; (Open Graph) or &lt;code&gt;fediverse&lt;/code&gt; metadata&lt;/li&gt;
&lt;li&gt;menu highlighting: the current page in the navigation menu is now highlighted&lt;/li&gt;
&lt;li&gt;lightweight template: added some variables in header and footer templates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Breaking:&lt;/strong&gt; previously, the &lt;code&gt;apex&lt;/code&gt; plugin rendered markdown metadata as small blocks at the top of the page by default. With this version, it will no longer render this metadata unless the markdown page explicitly includes &lt;code&gt;apexRenderMetadatas: true&lt;/code&gt; in its front matter.&lt;/p&gt;

&lt;h3 id=&quot;and-others-useful-updates&quot;&gt;And others useful updates&lt;/h3&gt;

&lt;p&gt;Although this article is already quite long, I want to briefly mention several smaller yet important tasks completed in this release.&lt;/p&gt;

&lt;h4 id=&quot;forgeconf-update&quot;&gt;ForgeConf update&lt;/h4&gt;

&lt;p&gt;The parameters &lt;code&gt;externalsshaddr&lt;/code&gt; (default value &lt;code&gt;ssh://localhost:4545/&lt;/code&gt;) and &lt;code&gt;externalhttpaddr&lt;/code&gt; (default value &lt;code&gt;http://localhost:4546/&lt;/code&gt;) have been added to ensure the display of correct external urls to users. These addresses are notably used by the &lt;code&gt;apex&lt;/code&gt; plugin generate proper public links. They are kept separate from the internal &lt;code&gt;sshaddr&lt;/code&gt; and &lt;code&gt;httpaddr&lt;/code&gt; because your GitRoot instance might be configured to listen internally (on &lt;code&gt;127.0.0.1&lt;/code&gt;) while being reachable externally through a reverse proxy.&lt;/p&gt;

&lt;h4 id=&quot;integrated-updater&quot;&gt;Integrated updater&lt;/h4&gt;

&lt;p&gt;I&amp;rsquo;ve added a preliminary implementation for an integrated updater. This feature will handle breaking changes gracefully. For this release, I&amp;rsquo;ve added some new parameters to &lt;code&gt;forgeConf.yml&lt;/code&gt;, the integrated updater will handle these by setting standard values that should not affect your current user instance setup.&lt;/p&gt;

&lt;h4 id=&quot;rust-and-wasm&quot;&gt;Rust and WASM&lt;/h4&gt;

&lt;p&gt;While integrating Rust, I discovered that the WASM (and specifically WASI Preview 1) target does not permit exporting the standard &lt;code&gt;free&lt;/code&gt; and &lt;code&gt;malloc&lt;/code&gt; functions, which GitRoot requires. I&amp;rsquo;ve added an option to rename these functions to &lt;code&gt;gitrootAlloc&lt;/code&gt; and &lt;code&gt;gitrootFree&lt;/code&gt;, resolving this compatibility issue.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.3.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.2.0.md</link><title>0.2.0</title><pubDate>2025/09/18</pubDate><description>&lt;h1 id=&quot;version-0-2-0-is-here&quot;&gt;Version 0.2.0 is here&lt;/h1&gt;

&lt;p&gt;In this version, my main focus was on stabilization. I fixed over 14 issues, some of which were identified before version 0.1.0, but a significant proportion were reported by YOU. A big thanks to everyone who took part of it in any way.&lt;/p&gt;

&lt;p&gt;Every time I shared gitroot, I came across new issues with the documentation and binaries, but also questions such as &amp;ldquo;how can I compile it?&amp;rdquo; or &amp;ldquo;what&amp;rsquo;s the next step?&amp;rdquo;. And I&amp;rsquo;m delighted to see your enthusiasm for it!&lt;/p&gt;

&lt;h2 id=&quot;first-contribution&quot;&gt;First contribution&lt;/h2&gt;

&lt;p&gt;Although all my new contributions were related to &amp;ldquo;bug fixes&amp;rdquo;, some of them required new features.&lt;/p&gt;

&lt;h3 id=&quot;no-notification-no-contribution&quot;&gt;No notification == no contribution&lt;/h3&gt;

&lt;p&gt;The first amusing problem came from the first contributor! Fifteen days later, he contacted me to say, &amp;ldquo;Hi, I pushed a branch where I corrected a lot of typos, but&amp;hellip; nothing has happened since I pushed it?&amp;rdquo;. In fact, I wasn&amp;rsquo;t actually aware that a new branch had been pushed. I had forgotten to implement a mechanism that would notify me whenever a new contribution is published!&lt;/p&gt;

&lt;p&gt;A simple way to achieve this is to add an RSS feed (yes, I was inspired by Radicle for this feature). This is ideal, because all I have to do is add an &amp;ldquo;rss.xml&amp;rdquo; file to the web folder&amp;hellip;and the current API of plugin 0.1.0 allows me to do this! So I created the &lt;code&gt;pollen&lt;/code&gt; plugin. It takes all the commits from all branches and adds an &lt;code&gt;rss:item&lt;/code&gt; to a page.&lt;/p&gt;

&lt;p&gt;However, in order to achieve this, I opted to experiment with a different programming language (all the plugins are developed in Golang). I chose &lt;code&gt;assemblyscript&lt;/code&gt;, which is a subset of the &lt;code&gt;typescript&lt;/code&gt; language. So I added a new &lt;code&gt;plugin sdk&lt;/code&gt; in typescript.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To follow all gitroot commits follow &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/rss/all-commits.xml&quot;&gt;https://gitroot.dev/rss/all-commits.xml&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;lot-of-bugs&quot;&gt;Lot of bugs&lt;/h3&gt;

&lt;p&gt;During this first contribution, I discovered numerous bugs in the “grafter” plugin. From the &lt;a href=&quot;issues/close/v0.2.0/73e8-graft-diff-not-working.html&quot;&gt;diff rendering&lt;/a&gt; to &lt;a href=&quot;issues/close/v0.2.0/e188-graft-merge-not-wanted.html&quot;&gt;unwanted merging&lt;/a&gt; and &lt;a href=&quot;issues/close/9422-graft-need-3-review.html&quot;&gt;very complicated final merging&lt;/a&gt;, nothing worked! Even the basic GitRoot features were painful to use :D&lt;/p&gt;

&lt;h2 id=&quot;let-s-the-show-continue&quot;&gt;Let&amp;rsquo;s the show continue&lt;/h2&gt;

&lt;h3 id=&quot;no-linux-no-gitroot&quot;&gt;No linux == no gitroot&lt;/h3&gt;

&lt;p&gt;Surprisingly, someone wanted to compile gitroot on a Mac. Well, gitroot is actually written in Go, so there&amp;rsquo;s no problem doing that&amp;hellip; Except that I forgot to publish a fix I made to a dependency. Go couldn&amp;rsquo;t download the right dependency and therefore fails to compile completely&amp;hellip; But everything is OK now! I added an automatic compilation for Mac, you can find this on the &lt;a href=&quot;CHANGELOG.html&quot;&gt;version&lt;/a&gt; page.&lt;/p&gt;

&lt;h3 id=&quot;doc-doc-doc&quot;&gt;Doc, doc&amp;hellip; doc?&lt;/h3&gt;

&lt;p&gt;After that, someone tried to run gitroot on their local computer, as described in the &lt;a href=&quot;doc/tutorials/from_zero_to_hero.html&quot;&gt;document&lt;/a&gt;. However, when version &lt;code&gt;0.1.0&lt;/code&gt; was released, I discovered a bug. Gitroot cannot know where the &lt;code&gt;root&lt;/code&gt; repository is located (because you can change its name). To fix this, I added a configuration file before initialization, which had to be passed on launch&amp;hellip; It&amp;rsquo;s my fault, I forgot to update this tutorial.&lt;/p&gt;

&lt;h3 id=&quot;a-lot-more&quot;&gt;A lot more&lt;/h3&gt;

&lt;p&gt;As you can see, there were many mistakes in the first version. However, they have all been fixed, and, of course, I have added plenty more to keep me busy for the next version!&lt;/p&gt;

&lt;h3 id=&quot;more-technicals-bugs&quot;&gt;More technicals bugs&lt;/h3&gt;

&lt;p&gt;But I also fixed several important issues that were the result of some shortcuts that every project has to take for publication. One was about a &lt;a href=&quot;issues/close/v0.2.0/95b0-plugins-fs-bug.html&quot;&gt;FS bug&lt;/a&gt; when directories were empty, another concerned the &lt;a href=&quot;issues/close/v0.2.0/f1ab-garbage-collecte-plugins.html&quot;&gt;garbage collect plugin&lt;/a&gt; when the plugin had finished running, and many others.&lt;/p&gt;

&lt;h2 id=&quot;simplify-plugin-install&quot;&gt;Simplify plugin install&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;0.1.0&lt;/code&gt; version, to install a plugin you should download it, tell to gitroot where it is and then after push/pull activate/configure it. Now it is way more simple, just give an url, like &lt;code&gt;https://gitroot.dev/releases/0.2.0/pollen-0.0.1.wasm&lt;/code&gt; and optionnally (but encouraged) a checksum &lt;code&gt;sha256: 49cd8e1846b1612a0d5686918b502fe29e070e7bf9613c1ba9a1dfe5417d0f2a&lt;/code&gt;. Gitroot will download, check and run it. Of course you need to activate it. All url and checksum are in the &lt;a href=&quot;CHANGELOG.html&quot;&gt;versions page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have added a mecanisme to check that the plugin you try to run is compatble with your version of GitRoot. So launching &lt;code&gt;ladybug-0.0.1.wasm&lt;/code&gt; in &lt;code&gt;gitroot-0.2.0&lt;/code&gt; will log an error and not run the wasm. As GitRoot is in alpha version all new gitroot version will need plugins update.&lt;/p&gt;

&lt;h2 id=&quot;what-next&quot;&gt;What next?&lt;/h2&gt;

&lt;p&gt;With the exception of any new bugs detected for the next iteration, I would like to focus on the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;creating a plugins marketplace&lt;/li&gt;
&lt;li&gt;adding a new language SDK for plugins (Maybe Rust? Or Zig?)&lt;/li&gt;
&lt;li&gt;rebasing the Git lib fork and applying patches&lt;/li&gt;
&lt;/ul&gt;
</description><guid>https://gitroot.dev//blog/version-0.2.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/version-0.1.0.md</link><title>0.1.0</title><pubDate>2025/09/18</pubDate><description>&lt;h1 id=&quot;gitroot-0-1-0-is-deployed&quot;&gt;GitRoot 0.1.0 is deployed 🎉��&lt;/h1&gt;

&lt;h2 id=&quot;gitroot-in-numbers&quot;&gt;GitRoot in numbers:&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;58 commits&lt;/li&gt;
&lt;li&gt;+500 files &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/&quot;&gt;https://gitroot.dev/worktree/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;1 issue in gitlab (closed) &lt;a target=&quot;_blank&quot; href=&quot;https://gitlab.com/gitroot/gitroot/-/issues/1&quot;&gt;https://gitlab.com/gitroot/gitroot/-/issues/1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;29 issues close &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/boards/closed.html&quot;&gt;https://gitroot.dev/boards/closed.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;43 issues open &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/boards/issues.html&quot;&gt;https://gitroot.dev/boards/issues.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;22 pages de doc &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/doc/&quot;&gt;https://gitroot.dev/doc/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;4 licences &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/README.html#license&quot;&gt;https://gitroot.dev/README.html#license&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;4 plugins &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/CHANGELOG.html#plugins&quot;&gt;https://gitroot.dev/CHANGELOG.html#plugins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But if you retain only one, it is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 contributor &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/.gitroot/users.yml.html&quot;&gt;https://gitroot.dev/worktree/.gitroot/users.yml.html&lt;/a&gt; (others are forge and plugins 😭)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So there is only one thig to do&amp;hellip; read the doc and contribute!&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/version-0.1.0.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/security-vulnerability-1.md</link><title>blog/security-vulnerability-1.md</title><pubDate>2026/03/30</pubDate><description>&lt;h1 id=&quot;security-advisory-executor-command-injection&quot;&gt;Security Advisory: Executor Command Injection&lt;/h1&gt;

&lt;h2 id=&quot;1-executive-summary&quot;&gt;1. Executive Summary&lt;/h2&gt;

&lt;p&gt;A vulnerability has been identified within the GitRoot &lt;strong&gt;executors&lt;/strong&gt; system. Under specific conditions, it is possible for a malicious actor to execute unauthorized commands on the host machine. Given the complexity of the required conditions, this vulnerability is not easily exploitable in a standard production environment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Severity:&lt;/strong&gt; Moderate&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Affected Version:&lt;/strong&gt; 0.3.0&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fix Status:&lt;/strong&gt; Scheduled for version 0.4.0&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;2-technical-details&quot;&gt;2. Technical Details&lt;/h2&gt;

&lt;p&gt;The vulnerability stems from the way executors validate commands defined by plugins. Currently, a plugin can declare a benign command in its manifest (e.g., &lt;code&gt;cat&lt;/code&gt;), but execute an arbitrary command during runtime (e.g., &lt;code&gt;rm -rf data/&lt;/code&gt;). GitRoot currently lacks a strict enforcement mechanism to verify that the executed binary matches the declared command.&lt;/p&gt;

&lt;h2 id=&quot;3-impact-analysis&quot;&gt;3. Impact Analysis&lt;/h2&gt;

&lt;p&gt;The level of risk depends on your infrastructure configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;High Risk:&lt;/strong&gt; Users utilizing &lt;code&gt;bareMetal&lt;/code&gt; or &lt;code&gt;ssh&lt;/code&gt; executors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Low Risk:&lt;/strong&gt; Users utilizing &lt;code&gt;bubblewrap&lt;/code&gt; or &lt;code&gt;container&lt;/code&gt; isolation (as the sandbox significantly limits the potential impact on the host).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exploitability Constraints:&lt;/strong&gt;
This vulnerability is not a &amp;ldquo;remote code execution&amp;rdquo; flaw in the traditional sense. Exploitation requires:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Installing a malicious plugin.&lt;/li&gt;
&lt;li&gt;Having write access to the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt; of the root repository.&lt;/li&gt;
&lt;li&gt;Merging a malicious &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt; into the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Currently, creating plugins is undocumented, and the only known plugin using executors is &lt;code&gt;hop&lt;/code&gt;. Since any modification to &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt; requires a merge into the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt;, a standard code review process should easily detect and prevent such malicious entries.&lt;/p&gt;

&lt;h2 id=&quot;4-immediate-mitigations&quot;&gt;4. Immediate Mitigations&lt;/h2&gt;

&lt;p&gt;If you cannot wait for the 0.4.0 release, we recommend applying the following measures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Disable Executors:&lt;/strong&gt; Set your forge configuration for executors to &lt;code&gt;None&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secure Executors:&lt;/strong&gt; Use more secure executors, such as &lt;code&gt;bwrap&lt;/code&gt; or &lt;code&gt;container&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plugin Trust:&lt;/strong&gt; Only install and enable plugins from trusted sources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access Control:&lt;/strong&gt; Strictly monitor and review all changes to &lt;code&gt;.gitroot/plugins.yml&lt;/code&gt;, specifically focusing on authorized system commands.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;5-patch-schedule&quot;&gt;5. Patch Schedule&lt;/h2&gt;

&lt;p&gt;This vulnerability will be fully addressed in &lt;strong&gt;version 0.4.0&lt;/strong&gt;.
We have decided not to release an intermediate 0.3.1 patch, as the attack vector relies on an &amp;ldquo;insider&amp;rdquo; or &amp;ldquo;compromised account&amp;rdquo; scenario that is effectively mitigated by standard code review practices.&lt;/p&gt;

&lt;h2 id=&quot;6-security-contact&quot;&gt;6. Security Contact&lt;/h2&gt;

&lt;p&gt;Transparency and security are core values at GitRoot. If you discover any other potential vulnerabilities or have questions regarding this report, please contact us directly at &lt;a href=&quot;blog/contact@gitroot.dev&quot;&gt;contact@gitroot.dev&lt;/a&gt;.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/security-vulnerability-1.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/modifying-API-SDK-plugin.md</link><title>Modifying the API in the SDK Plugin</title><pubDate>2026/04/06</pubDate><description>&lt;h1 id=&quot;modifying-the-api-in-the-sdk-plugin&quot;&gt;Modifying the API in the SDK Plugin&lt;/h1&gt;

&lt;p&gt;The last feature for version 0.4 is about adding &lt;code&gt;mv&lt;/code&gt; (move) and &lt;code&gt;cp&lt;/code&gt; (copy) functions to the SDK plugin. I&amp;rsquo;m taking advantage of this development to explain how the plumbing works under the hood.&lt;/p&gt;

&lt;p&gt;Right now, the SDK plugin is undocumented because there are a lot of breaking changes coming. Every time I develop a new plugin, I discover better ways to do things, and the SDK needs to adapt.&lt;/p&gt;

&lt;p&gt;For example, when I added &lt;code&gt;apex_code&lt;/code&gt; (a plugin that takes code as a string and returns syntax-highlighted HTML), I wondered: &amp;ldquo;Is this a standalone plugin?&amp;rdquo;. Not really, because there&amp;rsquo;s no file to manipulate, neither in input nor output. Instead, it manages blocks inside an existing file. The SDK wasn&amp;rsquo;t originally built with that use case in mind.&lt;/p&gt;

&lt;p&gt;Anyway, I think explaining how the SDK works through a concrete example will be helpful, if not for you, at least for me.&lt;/p&gt;

&lt;h2 id=&quot;what-is-the-sdk-s-job&quot;&gt;What is the SDK&amp;rsquo;s job?&lt;/h2&gt;

&lt;p&gt;The SDK exists to pass data from GitRoot (the host) to the plugin (the guest). As you know, GitRoot uses WebAssembly (WASM) to manage plugins. Unfortunately, WASM is still a bit low-level to implement: between the host and the guest, only integers can travel.&lt;/p&gt;

&lt;p&gt;Wait, only integers?&lt;/p&gt;

&lt;p&gt;Yes. So, how do we send a string? The host can store any kind of data in the guest&amp;rsquo;s memory. GitRoot stores a string, retrieves the pointer (the memory address) and the size, and sends those two integers to the guest. The guest then retrieves the string from its own memory. It&amp;rsquo;s a complex dance just for a simple string. The SDK is here to hide all that mess.&lt;/p&gt;

&lt;p&gt;When you build a plugin, you just implement an interface. The SDK handles the rest: you receive objects and strings, and you send back data without ever worrying about the underlying WASM pointers.&lt;/p&gt;

&lt;h2 id=&quot;what-does-a-concrete-implementation-look-like&quot;&gt;What does a concrete implementation look like?&lt;/h2&gt;

&lt;p&gt;When I add a new function, I always start with the host side. All functions are located in &lt;a href=&quot;app/server/plugin/runtime.go&quot;&gt;runtime.go&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We need to register the function in &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/wazero/wazero&quot;&gt;wazero&lt;/a&gt;, our WASM runtime. I add two versions because I haven&amp;rsquo;t found a clean way to bridge the difference between AssemblyScript and Go/Rust yet. Hopefully, I&amp;rsquo;ll figure that out one day.&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;r.wazRun.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#50fa7b&quot;&gt;NewFunctionBuilder&lt;/span&gt;().&lt;span style=&quot;color:#50fa7b&quot;&gt;WithFunc&lt;/span&gt;(r.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(&lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;)).&lt;span style=&quot;color:#50fa7b&quot;&gt;Export&lt;/span&gt;(&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;moveFile&amp;#34;&lt;/span&gt;).
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#50fa7b&quot;&gt;NewFunctionBuilder&lt;/span&gt;().&lt;span style=&quot;color:#50fa7b&quot;&gt;WithFunc&lt;/span&gt;(r.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(&lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;)).&lt;span style=&quot;color:#50fa7b&quot;&gt;Export&lt;/span&gt;(&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;moveFileAS&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I implement the builder:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(forAS &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;interface&lt;/span&gt;{} {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; !forAS {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, ptr, size, ptr2, size2 &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#6272a4&quot;&gt;// from ptr/size to string or object
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;			r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromPath, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	} &lt;span style=&quot;color:#ff79c6&quot;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, ptr, ptr2 &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#6272a4&quot;&gt;// from ptr to string or object
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;			r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromPath, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Yeah, I know. As a Gopher, seeing &lt;code&gt;interface{}&lt;/code&gt; as a return type and all these uint32 feels wrong. But hey, we&amp;rsquo;re not here for a code review, we&amp;rsquo;re here to add an API. If you have a cleaner way, feel free to submit a graft!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We want to allow any plugin to move a file. Let&amp;rsquo;s define the internal &lt;code&gt;moveFile&lt;/code&gt; function:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromPath, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#6272a4&quot;&gt;//need to do something...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Actually, there&amp;rsquo;s a catch, GitRoot uses three internal filesystems: one for the git worktree, one for web content, and one for the cache. Even if they are abstracted under one root, the code often expects a specific FS. I&amp;rsquo;ll probably clean this up later, but for now:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#6272a4&quot;&gt;//need to do something...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now your plugin can call &lt;code&gt;moveFile(&amp;quot;cache&amp;quot;, &amp;quot;artifacts/myBin&amp;quot;, &amp;quot;web&amp;quot;, &amp;quot;releases/myBin-v1.0&amp;quot;)&lt;/code&gt; to publish an artifact, or &lt;code&gt;moveFile(&amp;quot;web&amp;quot;, &amp;quot;index.html&amp;quot;, &amp;quot;web&amp;quot;, &amp;quot;old/index.html&amp;quot;)&lt;/code&gt; to back up a file.&lt;/p&gt;

&lt;p&gt;I won&amp;rsquo;t bore you with the implementation logic here. If you&amp;rsquo;re curious, check the &lt;a href=&quot;app/server/plugin/runtime.go&quot;&gt;code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is how the final builder looks, converting pointers to strings:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (r &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;runtime) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileBuilder&lt;/span&gt;(forAS &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;interface&lt;/span&gt;{} {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; !forAS {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, fromFsPtr, fromFsSize, fromPathPtr, fromGlobPathSize, toFsPtr, toFsSize, toPathPtr, toPathSize &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;uint64&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile fromFs&amp;#34;&lt;/span&gt;, fromFsPtr, fromFsSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile fromGlobPath&amp;#34;&lt;/span&gt;, fromPathPtr, fromGlobPathSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile toFs&amp;#34;&lt;/span&gt;, toFsPtr, toFsSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFile toPath&amp;#34;&lt;/span&gt;, toPathPtr, toPathSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	} &lt;span style=&quot;color:#ff79c6&quot;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt;(_ context.Context, m api.Module, fromFsPtr, fromPathPtr, toFsPtr, toPathPtr &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;uint64&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS fromFs&amp;#34;&lt;/span&gt;, fromFsPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			fromPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS fromGlobPath&amp;#34;&lt;/span&gt;, fromPathPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toFs, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS toFs&amp;#34;&lt;/span&gt;, toFsPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			toPath, err &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;readASString&lt;/span&gt;(m, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;MoveFileAS toPath&amp;#34;&lt;/span&gt;, toPathPtr)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; err &lt;span style=&quot;color:#ff79c6&quot;&gt;!=&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L38&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L38&quot;&gt;38&lt;/a&gt;&lt;/span&gt;&lt;span&gt;				&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L39&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L39&quot;&gt;39&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L40&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L40&quot;&gt;40&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; r.&lt;span style=&quot;color:#50fa7b&quot;&gt;sendEmptyOrError&lt;/span&gt;(m, r.&lt;span style=&quot;color:#50fa7b&quot;&gt;moveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L41&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L41&quot;&gt;41&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L42&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L42&quot;&gt;42&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L43&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L43&quot;&gt;43&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When I need to pass complex objects, I use &lt;code&gt;json.Marshal&lt;/code&gt; in GitRoot and &lt;code&gt;json.Unmarshal&lt;/code&gt; in the SDK. It works, but don&amp;rsquo;t get too attached to it, I might change the serialization method in the future.&lt;/p&gt;

&lt;h2 id=&quot;inside-the-sdk&quot;&gt;Inside the SDK&lt;/h2&gt;

&lt;p&gt;Now that the host is ready, we need to implement the other side: the SDK itself. I usually start with Go, then move to Rust and AssemblyScript. Sometimes I forget the others&amp;hellip; shame on me! I&amp;rsquo;m planning to automate this with some boilerplate generation later.&lt;/p&gt;

&lt;p&gt;In the &lt;a href=&quot;app/libs/golang/plugin/model/server.go&quot;&gt;server&lt;/a&gt; interface:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;type&lt;/span&gt; Server &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;interface&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#6272a4&quot;&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;    &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(fromFs, fromPath, toFs, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#6272a4&quot;&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make it more &amp;ldquo;user-friendly&amp;rdquo;, I use a struct to represent the filesystem in &lt;a href=&quot;app/libs/golang/plugin/model/fs.go&quot;&gt;model/fs.go&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (g &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;GrFs) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(fromPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; g.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(g.base, fromPath, g.base, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (g &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;GrFs) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileToFs&lt;/span&gt;(fromPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, toFs &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;GrFs, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; g.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(g.base, fromPath, toFs.base, toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The actual WASM call happens in &lt;a href=&quot;app/libs/golang/plugin/server.go&quot;&gt;plugin/server.go&lt;/a&gt;, where we transform strings back into pointers:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;// MoveFile implements [model.ServerNeeded].
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (s &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;pServer) &lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFile&lt;/span&gt;(fromFs model.FsBase, fromPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, toFs model.FsBase, toPath &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr, size &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;string&lt;/span&gt;(fromFs))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr2, size2 &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(fromPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr3, size3 &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;string&lt;/span&gt;(toFs))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	ptr4, size4 &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;stringToPtr&lt;/span&gt;(toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	resPtrSize &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;_moveFile&lt;/span&gt;(ptr, size, ptr2, size2, ptr3, size3, ptr4, size4)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(fromFs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(fromPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(toFs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	runtime.&lt;span style=&quot;color:#50fa7b&quot;&gt;KeepAlive&lt;/span&gt;(toPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;ptrSizeToError&lt;/span&gt;(resPtrSize)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally, the link to the host via &lt;code&gt;go:wasmimport&lt;/code&gt; in &lt;a href=&quot;app/libs/golang/plugin/imports.go&quot;&gt;plugin/imports.go&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;//go:wasmimport gitroot moveFile
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;_moveFile&lt;/span&gt;(fromFsPtr, fromFsSize, fromPathPtr, fromPathSize, toFsPtr, toFsSize, toPathPtr, toPathSize &lt;span style=&quot;color:#8be9fd&quot;&gt;uint32&lt;/span&gt;) (ptrSize &lt;span style=&quot;color:#8be9fd&quot;&gt;uint64&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;how-to-use-it&quot;&gt;How to use it?&lt;/h2&gt;

&lt;p&gt;Now, any plugin can use this. Here&amp;rsquo;s a simple Go plugin that triggers a build whenever a &lt;code&gt;.go&lt;/code&gt; file is modified and moves the binary to the web folder.&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;package&lt;/span&gt; main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;path/filepath&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;strings&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	gitroot &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;gitroot.dev/libs/golang/plugin&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;gitroot.dev/libs/golang/plugin/model&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;var&lt;/span&gt; defaultRun = []model.PluginRun{{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	Path:   &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*.go&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	When:   model.PluginRunWhenAll,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	Branch: []&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;{&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	Write: model.PluginWrite{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		Web: []model.PluginWriteRight{{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			Path: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;releases/main&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			Can:  model.PluginWriteRightCanAll,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		}},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;type&lt;/span&gt; Plugin &lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	server model.Server
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    needToBuild &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;Init&lt;/span&gt;(repoName &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;, confHasChanged &lt;span style=&quot;color:#8be9fd&quot;&gt;bool&lt;/span&gt;, serializedConf &lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;StartCommit&lt;/span&gt;(commit model.Commit) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                  { &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;AddFile&lt;/span&gt;(file model.File) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L38&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L38&quot;&gt;38&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L39&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L39&quot;&gt;39&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L40&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L40&quot;&gt;40&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L41&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L41&quot;&gt;41&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;ModFile&lt;/span&gt;(file model.File) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L42&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L42&quot;&gt;42&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L43&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L43&quot;&gt;43&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L44&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L44&quot;&gt;44&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L45&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L45&quot;&gt;45&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L46&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L46&quot;&gt;46&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;DelFile&lt;/span&gt;(file model.File) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L47&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L47&quot;&gt;47&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	p.needToBuild = &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L48&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L48&quot;&gt;48&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L49&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L49&quot;&gt;49&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L50&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L50&quot;&gt;50&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L51&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L51&quot;&gt;51&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;EndCommit&lt;/span&gt;(commit model.Commit) &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                    { &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L52&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L52&quot;&gt;52&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L53&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L53&quot;&gt;53&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; (p &lt;span style=&quot;color:#ff79c6&quot;&gt;*&lt;/span&gt;Plugin) &lt;span style=&quot;color:#50fa7b&quot;&gt;Finish&lt;/span&gt;() &lt;span style=&quot;color:#8be9fd&quot;&gt;error&lt;/span&gt;                                                          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L54&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L54&quot;&gt;54&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;if&lt;/span&gt; p.needToBuild {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L55&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L55&quot;&gt;55&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        res, _ &lt;span style=&quot;color:#ff79c6&quot;&gt;:=&lt;/span&gt; p.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;Exec&lt;/span&gt;(model.Exec{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L56&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L56&quot;&gt;56&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            Cmds: []model.Cmd{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L57&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L57&quot;&gt;57&lt;/a&gt;&lt;/span&gt;&lt;span&gt;                {Cmd: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;go&amp;#34;&lt;/span&gt;, Args: []&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;{&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;build&amp;#34;&lt;/span&gt;}},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L58&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L58&quot;&gt;58&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L59&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L59&quot;&gt;59&lt;/a&gt;&lt;/span&gt;&lt;span&gt;            Artifacts: []&lt;span style=&quot;color:#8be9fd&quot;&gt;string&lt;/span&gt;{&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L60&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L60&quot;&gt;60&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L61&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L61&quot;&gt;61&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; p.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;Cache&lt;/span&gt;().&lt;span style=&quot;color:#50fa7b&quot;&gt;MoveFileToFs&lt;/span&gt;(res.Artifacts[&lt;span style=&quot;color:#bd93f9&quot;&gt;0&lt;/span&gt;], p.server.&lt;span style=&quot;color:#50fa7b&quot;&gt;Webcontent&lt;/span&gt;(), filepath.&lt;span style=&quot;color:#50fa7b&quot;&gt;Join&lt;/span&gt;(&lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;releases&amp;#34;&lt;/span&gt;, &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L62&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L62&quot;&gt;62&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L63&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L63&quot;&gt;63&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L64&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L64&quot;&gt;64&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L65&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L65&quot;&gt;65&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L66&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L66&quot;&gt;66&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;Build&lt;/span&gt;(server model.Server) model.Plugin {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L67&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L67&quot;&gt;67&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&quot;color:#ff79c6&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color:#ff79c6&quot;&gt;&amp;amp;&lt;/span&gt;Plugin{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L68&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L68&quot;&gt;68&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		server: server,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L69&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L69&quot;&gt;69&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L70&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L70&quot;&gt;70&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L71&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L71&quot;&gt;71&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L72&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L72&quot;&gt;72&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;//go:wasmexport install
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L73&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L73&quot;&gt;73&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#6272a4&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#8be9fd;font-style:italic&quot;&gt;func&lt;/span&gt; &lt;span style=&quot;color:#50fa7b&quot;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L74&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L74&quot;&gt;74&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	gitroot.&lt;span style=&quot;color:#50fa7b&quot;&gt;Register&lt;/span&gt;(defaultRun, Build)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L75&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L75&quot;&gt;75&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;is-that-all&quot;&gt;Is that all?&lt;/h2&gt;

&lt;p&gt;Nope. I still need to update the &lt;a href=&quot;app/libs/rust/plugin/src/fs.rs&quot;&gt;Rust&lt;/a&gt; and &lt;a href=&quot;app/libs/ts/plugin/fs.ts&quot;&gt;AssemblyScript&lt;/a&gt; SDKs and, of course, add &lt;a href=&quot;app/libs/golang/plugin/test/fakeserver.go&quot;&gt;tests&lt;/a&gt;. But this should give you a good idea of how the plumbing works.&lt;/p&gt;

&lt;p&gt;If anything is unclear, &lt;a href=&quot;contact.html&quot;&gt;let me know&lt;/a&gt;!&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/modifying-API-SDK-plugin.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/how-to-help_fr_11-2025.md</link><title>Comment contribuer au développement de GitRoot</title><pubDate>2025/11/18</pubDate><description>&lt;h1 id=&quot;comment-contribuer-au-développement-de-gitroot-11-2025&quot;&gt;Comment contribuer au développement de GitRoot 11-2025&lt;/h1&gt;

&lt;p&gt;Je viens tout juste de lire &lt;a target=&quot;_blank&quot; href=&quot;https://antonin.delpeuch.eu/posts/too-much-time-on-your-hands/&quot;&gt;cette article&lt;/a&gt; et je n&amp;rsquo;arrête pas d&amp;rsquo;y penser. Je le met en perspective des contributeurs de GitRoot. Je suis le seul contributeur (à part mon ami Damien qui a corrigé de nombreuses fautes, merci à lui) sur ce projet. Et comme le montre ce &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge/statuses/01K98BCFEB9V430JD9TFQ32YZN&quot;&gt;toot&lt;/a&gt; j&amp;rsquo;ai un travail pour payer les factures, 1 femme et 2 enfants qui m&amp;rsquo;occupent pratiquement tout le reste du temps. À ça je rajoute GitRoot, pour le moment je m&amp;rsquo;éclate et je tiens bon mais pour combien de temps je vais y arriver ?&lt;/p&gt;

&lt;p&gt;Alors pourquoi pas de contributeurs externes sur GitRoot ?&lt;/p&gt;

&lt;p&gt;Je me dit souvent que le projet est jeune, trop jeune, pour que ça soit accessible à un developpeur externe. Parfois je me dit qu&amp;rsquo;un meilleurs système de ticket serait nécessaire pour avoir un label &amp;ldquo;welcome new contribution&amp;rdquo;. Parfois je me dit qu&amp;rsquo;une fois que j&amp;rsquo;aurais déployé les plugins sdk et fait un market place de plugins, enfin vous pourrez venir m&amp;rsquo;aider à faire que GitRoot soit un vrai projet open-source.&lt;/p&gt;

&lt;p&gt;Mais, car il y a un mais, cette liste ne s&amp;rsquo;arrêtera jamais. Soyons franc il y aura toujours un truc de plus à faire avant que vous puissiez m&amp;rsquo;aider à faire que GitRoot soit au top. J&amp;rsquo;ai donc décidé de faire la chose la plus simple à l&amp;rsquo;heure actuelle pour vous expliquer comment m&amp;rsquo;aider: un article de blog.&lt;/p&gt;

&lt;p&gt;Ici, je vais vous expliquer tout ce qu&amp;rsquo;il est possible de faire en l&amp;rsquo;état actuel du projet. Pas ce qu&amp;rsquo;il sera possible de faire un jour &lt;em&gt;peu être&lt;/em&gt;, mais ce que vous pouvez faire là maintenant. Je réitèrerais ce genre d&amp;rsquo;article tant que nécessaire.&lt;/p&gt;

&lt;h2 id=&quot;pré-requis&quot;&gt;Pré-requis&lt;/h2&gt;

&lt;p&gt;Il n&amp;rsquo;y a pas grand chose à faire pour être prêt à aider sur GitRoot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;vous devez avoir &lt;a target=&quot;_blank&quot; href=&quot;https://git-scm.com/&quot;&gt;git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;git clone ssh://gitroot.dev&lt;/li&gt;
&lt;li&gt;cd gitroot.dev&lt;/li&gt;
&lt;li&gt;./gitroot/init.sh&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si vous souhaitez coder il vous faudra en plus:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/bash/manual/bash.html&quot;&gt;bash&lt;/a&gt; et &lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/make/&quot;&gt;make&lt;/a&gt; (si vous êtes dev il y a de grandes chances que vous les aillez déjà)&lt;/li&gt;
&lt;li&gt;installer &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt;, c&amp;rsquo;est le plus simple pour avoir tous les compilateurs dans les bonnes versions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Et voila vous êtes bon pour commencer à aider. Mais que faire ? Et bien comme tout bon developpeur je vais vous répondre &amp;ldquo;ça dépend&amp;rdquo;. Ça dépend de ce que vous voulez faire ! Je vais lister dessous un ensemble de taches possibles, vous choisissez.&lt;/p&gt;

&lt;h2 id=&quot;avant-de-commencer&quot;&gt;Avant de commencer&lt;/h2&gt;

&lt;p&gt;Avant de vous dire ce que vous pouvez faire, je me dit qu&amp;rsquo;il serait bien de vous expliquer ce que moi je fais. Donc pour cette version 0.3.0 ma priorité absolue est de faire en sorte que GitRoot build GitRoot lui même. Aujourd&amp;rsquo;hui à chaque release je fais les binaires sur ma macine et je les scp. Je veux qu&amp;rsquo;avec cette version 0.3.0 cela ne soit plus le cas.&lt;/p&gt;

&lt;p&gt;Donc, si j&amp;rsquo;arrive à me retenir, je ne devrais pas faire les tickets qui suivent. Vous avez tout le temps dont vous souhaitez pour les faire. Le plus simple est, je pense, de nous retrouver sur &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;matrix channel gitroot&lt;/a&gt; pour en discutter.&lt;/p&gt;

&lt;p&gt;On peut également en parler par mail &lt;a href=&quot;blog/contact@gitroot.dev&quot;&gt;contact@gitroot.dev&lt;/a&gt; ou sur le fediverse &lt;cite class=&quot;informative&quot;&gt;&lt;a href=&quot;#forge@gitrrot.dev&quot;&gt;&lt;sup&gt;[forge@gitrrot.dev]&lt;/sup&gt;&lt;/a&gt;&lt;/cite&gt;(&lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;https://gts.gitroot.dev/@forge&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;qu-est-ce-qu-on-peut-faire&quot;&gt;Qu&amp;rsquo;est-ce qu&amp;rsquo;on peut faire ?&lt;/h2&gt;

&lt;h3 id=&quot;corriger-les-fautes&quot;&gt;Corriger les fautes&lt;/h3&gt;

&lt;p&gt;L&amp;rsquo;anglais n&amp;rsquo;est pas ma langue d&amp;rsquo;origine et je pense qu&amp;rsquo;il y a beaucoup de fautes, donc une tache pourrais être de lire tous les md dans &lt;code&gt;/doc/&lt;/code&gt;, &lt;code&gt;/blog/&lt;/code&gt; et pourquoi pas &lt;code&gt;/issues/&lt;/code&gt;. Vous corrigez, faites une branche et poussez. Je m&amp;rsquo;occupe du reste !&lt;/p&gt;

&lt;h3 id=&quot;moi-je-veux-faire-du-web&quot;&gt;Moi je veux faire du web&lt;/h3&gt;

&lt;p&gt;Ce qui serait top, ce serait de créer (ou de trouver) un thème. Aujourd&amp;rsquo;hui GitRoot utilise &lt;a target=&quot;_blank&quot; href=&quot;https://simplecss.org/&quot;&gt;simplecss&lt;/a&gt;, et il n&amp;rsquo;a besoin que de &lt;code&gt;css classless&lt;/code&gt;, il en existe beaucoup mais vous pouvez aussi créer celui de GitRoot.&lt;/p&gt;

&lt;p&gt;Le plus simple pour essayer est de lancer la commande &lt;code&gt;make testsuite&lt;/code&gt; quand elle a fini rendez vous dans &lt;code&gt;cd /tmp/repo1&lt;/code&gt; éditez le fichier &lt;code&gt;style.css&lt;/code&gt; puis &lt;code&gt;git add .&lt;/code&gt; &lt;code&gt;git commit -m &amp;quot;try style&amp;quot;&lt;/code&gt; et &lt;code&gt;git push&lt;/code&gt;, ouvrez &lt;a target=&quot;_blank&quot; href=&quot;http://127.0.0.1:4546/repo1&quot;&gt;http://127.0.0.1:4546/repo1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Quand vous êtes content faites une branche avec votre css dans &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/resources/styles/&quot;&gt;apex&lt;/a&gt;, puis &lt;code&gt;git add .&lt;/code&gt; et &lt;code&gt;git push&lt;/code&gt; je m&amp;rsquo;occupe du reste.&lt;/p&gt;

&lt;h3 id=&quot;moi-je-veux-coder&quot;&gt;Moi je veux coder&lt;/h3&gt;

&lt;p&gt;Bien, pas de soucis, voici une liste de petites tâches (enfin elles me paraissent petites mais si c&amp;rsquo;est pas le cas pour vous dites le moi):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;en golang:

&lt;ul&gt;
&lt;li&gt;dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/grafter/&quot;&gt;grafter&lt;/a&gt; pouvoir afficher &lt;a href=&quot;issues/3d33-grafter-dco.html&quot;&gt;autre chose&lt;/a&gt; dans le 1er message d&amp;rsquo;un graft. La bannière est en dure dans le plugin, il faudrait l&amp;rsquo;exporter dans la conf du plugin et l&amp;rsquo;utiliser.&lt;/li&gt;
&lt;li&gt;dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/silo/&quot;&gt;silo&lt;/a&gt; permettre de &lt;a href=&quot;issues/76ec-silo-more-selects.html&quot;&gt;grouper par fichier ou date&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;plus difficile mais j&amp;rsquo;aimerais bien avoir plus de &lt;a href=&quot;issues/e6d8-apex-alert-support.html&quot;&gt;renderer&lt;/a&gt; dans &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/&quot;&gt;apex&lt;/a&gt;, par exemple les infos, warning et autre&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;en typescript:

&lt;ul&gt;
&lt;li&gt;dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; il y a un bug sur le rss &lt;a href=&quot;issues/3986-pollen-rss-grow-infinite.html&quot;&gt;il est jamais nettoyé&lt;/a&gt;. Rajouté une option (ou pas, 100 en dur me semble être déjà pas mal) pour limiter le nombre d&amp;rsquo;&lt;code&gt;&amp;lt;item&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;toujours dans le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; pourquoi pas rajouter un &lt;a target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/JSON_Feed&quot;&gt;json feed&lt;/a&gt; en plus du rss. On peut serializer du json en assemblescript.&lt;/li&gt;
&lt;li&gt;plus difficile mais il serait bien de pouvoir dire au plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; de générer un rss avec le contenu des fichiers. Ça permettrait à GitRoot d&amp;rsquo;avoir un rss affichant les articles du blog par exemple. Aujourd&amp;rsquo;hui il ne met dans le rss que les commits.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;en rust:

&lt;ul&gt;
&lt;li&gt;faire ce que Ygor à dit dans &lt;a target=&quot;_blank&quot; href=&quot;https://floss.social/@ygor/115474535138702961&quot;&gt;https://floss.social/@ygor/115474535138702961&lt;/a&gt; je ne suis pas un developpeur rust et transformer des &lt;code&gt;String&lt;/code&gt; en &lt;code&gt;&amp;amp;str&lt;/code&gt; me parle pas beaucoup mais si vous l&amp;rsquo;êtes je suis sûr que c&amp;rsquo;est une petite tâche. Le code se trouve dans le &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; et le plugin &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/hop/&quot;&gt;hop&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;ajouter un &lt;a href=&quot;issues/9f00-rust-sdk-fake-server-test.html&quot;&gt;fakeServer&lt;/a&gt; au &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; pour faciliter les tests d&amp;rsquo;un plugin, voici celui de go &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&quot;&gt;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;moi-je-suis-sysadmin&quot;&gt;Moi je suis sysadmin&lt;/h3&gt;

&lt;p&gt;Parfait il y a de quoi faire aussi:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;aujourd&amp;rsquo;hui il y a trop d&amp;rsquo;étapes pour releaser GitRoot, &lt;a href=&quot;issues/dba0-simplify-release.html&quot;&gt;automatiser&lt;/a&gt; au maximum m&amp;rsquo;aiderais beaucoup.&lt;/li&gt;
&lt;li&gt;si vous avez un petit de place sur un de vos serveurs, &lt;a href=&quot;doc/tutorials/deploy_instance.html&quot;&gt;tester de déployer une instance&lt;/a&gt; juste pour le fun. Peu être vous pourrez améliorer la doc ou trouver quelque chose qui ne marche plus. Ou ajouter un paragraphe pour utiliser gitroot avec nginx ou autre.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;moi-je-veux-tester&quot;&gt;Moi je veux tester&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;tous les tests sont en bash dans &lt;a href=&quot;app/testsuite/runtest.sh&quot;&gt;testsuite/runtest.sh&lt;/a&gt; se trouve le plus gros test, à côté il y en d&amp;rsquo;autres

&lt;ul&gt;
&lt;li&gt;exécutez les pour voir s&amp;rsquo;ils marchent chez vous me parait une bonne première tâche&lt;/li&gt;
&lt;li&gt;en créer de nouveaux&lt;/li&gt;
&lt;li&gt;améliorer les existants en sortant par exemple des fonctions helper comme &lt;code&gt;quiet_git()&lt;/code&gt;, &lt;code&gt;report()&lt;/code&gt; ou &lt;code&gt;mySleep()&lt;/code&gt; dans d&amp;rsquo;autres fichiers (et les importer) serait d&amp;rsquo;une grande aide pour le projet. Vous pouvez aussi créer des nouveaux pour &lt;code&gt;killGitroot()&lt;/code&gt; qui est fait en début de chaque fichier. Ou &lt;code&gt;createProject()&lt;/code&gt; qui consiste à chaque fois à aller dans le root repository et ajouter son nom dans le fichier &lt;code&gt;.gitroot/repositories.yml&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;moi-je-veux-juste-réfléchir-discutter&quot;&gt;Moi je veux juste réfléchir/discutter&lt;/h3&gt;

&lt;p&gt;Tu es le bienvenue sur le &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;channel gitroot&lt;/a&gt; dans matrix. Ou par mail &lt;code&gt;contact [AT] gitroot.dev&lt;/code&gt; ou sur le fediverse &lt;code&gt;@forge@gitroot.dev&lt;/code&gt; &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;sur ce serveur&lt;/a&gt;.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/how-to-help_fr_11-2025.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/how-to-help_en_11-2025.md</link><title>How to help building GitRoot</title><pubDate>2025/11/18</pubDate><description>&lt;h1 id=&quot;how-to-help-building-gitroot-11-2025&quot;&gt;How to help building GitRoot 11-2025&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;This article is the translation of &lt;a href=&quot;blog/how-to-help_fr_11-2025.html&quot;&gt;french version&lt;/a&gt; thanks to &lt;a target=&quot;_blank&quot; href=&quot;https://www.deepl.com&quot;&gt;DeepL&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I just read &lt;a target=&quot;_blank&quot; href=&quot;https://antonin.delpeuch.eu/posts/too-much-time-on-your-hands/&quot;&gt;this article&lt;/a&gt; and I can&amp;rsquo;t stop thinking about it. I&amp;rsquo;m putting it into perspective for GitRoot contributors. I&amp;rsquo;m the only contributor (apart from my friend Damien, who corrected numerous typos, thank you to him) on this project. And as this &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge/statuses/01K98BCFEB9V430JD9TFQ32YZN&quot;&gt;toot&lt;/a&gt; shows, I have a job to pay the bills, a wife and two children who take up practically all the rest of my time. On top of that, I have GitRoot. For now, I&amp;rsquo;m having a blast and hanging in there, but how long can I keep it up?&lt;/p&gt;

&lt;p&gt;So why not have external contributors on GitRoot?&lt;/p&gt;

&lt;p&gt;I often tell myself that the project is young, too young, to be accessible to an external developer. Sometimes I think that a better issues system would be necessary to have a “welcome new contribution” label. Sometimes I think that once I&amp;rsquo;ve deployed the SDK plugins and created a plugin marketplace, then you can come and help me make GitRoot a true open-source project.&lt;/p&gt;

&lt;p&gt;But, because there is a but, this list will never end. Let&amp;rsquo;s be honest, there will always be something else to do before you can help me make GitRoot the best it can be. So I decided to do the simplest thing possible right now to explain how you can help me: a blog post.&lt;/p&gt;

&lt;p&gt;Here, I&amp;rsquo;ll explain everything that can be done in the current state of the project. Not what might be possible someday, but what you can do right now. I&amp;rsquo;ll repeat this kind of article as often as necessary.&lt;/p&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;There isn&amp;rsquo;t much you need to do to get ready to help out on GitRoot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you need to have &lt;a target=&quot;_blank&quot; href=&quot;https://git-scm.com/&quot;&gt;git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;git clone ssh://gitroot.dev&lt;/li&gt;
&lt;li&gt;cd gitroot.dev&lt;/li&gt;
&lt;li&gt;./gitroot/init.sh&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to code, you will also need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/bash/manual/bash.html&quot;&gt;bash&lt;/a&gt; and &lt;a target=&quot;_blank&quot; href=&quot;https://www.gnu.org/software/make/&quot;&gt;make&lt;/a&gt; (if you are a developer, chances are you already have them)&lt;/li&gt;
&lt;li&gt;Install &lt;a target=&quot;_blank&quot; href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt;, which is the easiest way to get all the right versions of the compilers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And now you&amp;rsquo;re ready to start helping. But what should you do? Well, like any good developer, I&amp;rsquo;ll answer, &amp;ldquo;It depends.&amp;rdquo;. It depends on what you want to do! I&amp;rsquo;ll list a set of possible tasks below, and you can choose.&lt;/p&gt;

&lt;h2 id=&quot;before-we-begin&quot;&gt;Before we begin&lt;/h2&gt;

&lt;p&gt;Before telling you what you can do, I thought it would be a good idea to explain what I am doing. So, for this version 0.3.0, my top priority is to make sure that GitRoot builds GitRoot itself. Currently, for each release, I build the binaries on my machine and then scp them. I want this to no longer be the case with version 0.3.0.&lt;/p&gt;

&lt;p&gt;So, if I can restrain myself, I shouldn&amp;rsquo;t have to do the following tickets. You have all the time you need to do them. The easiest thing, I think, is to meet up on &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;matrix channel gitroot&lt;/a&gt; to discuss them.&lt;/p&gt;

&lt;p&gt;We can also talk about it by email &lt;a href=&quot;blog/contact@gitroot.dev&quot;&gt;contact@gitroot.dev&lt;/a&gt; or on the fediverse &lt;cite class=&quot;informative&quot;&gt;&lt;a href=&quot;#forge@gitrrot.dev&quot;&gt;&lt;sup&gt;[forge@gitrrot.dev]&lt;/sup&gt;&lt;/a&gt;&lt;/cite&gt;(&lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;https://gts.gitroot.dev/@forge&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;what-can-we-do&quot;&gt;What can we do?&lt;/h2&gt;

&lt;h3 id=&quot;correct-mistakes&quot;&gt;Correct mistakes&lt;/h3&gt;

&lt;p&gt;English is not my native language, and I think there are a lot of typos, so one task could be to read all the MD files in &lt;code&gt;/doc/&lt;/code&gt;, &lt;code&gt;/blog/&lt;/code&gt;, and maybe even &lt;code&gt;/issues/&lt;/code&gt;. Correct them, create a branch, and push. I&amp;rsquo;ll take care of the rest!&lt;/p&gt;

&lt;h3 id=&quot;i-want-to-work-on-the-web&quot;&gt;I want to work on the web&lt;/h3&gt;

&lt;p&gt;It would be great to create (or find) a theme. GitRoot currently uses &lt;a target=&quot;_blank&quot; href=&quot;https://simplecss.org/&quot;&gt;simplecss&lt;/a&gt;, and it only needs &lt;code&gt;classless CSS&lt;/code&gt;. There are many available, but you can also create one for GitRoot.&lt;/p&gt;

&lt;p&gt;The easiest way to try it out is to run the &lt;code&gt;make testsuite&lt;/code&gt; command. When it&amp;rsquo;s finished, go to &lt;code&gt;cd /tmp/repo1&lt;/code&gt;, edit the &lt;code&gt;style.css&lt;/code&gt; file, then &lt;code&gt;git add .&lt;/code&gt; &lt;code&gt;git commit -m &amp;quot;try style&amp;quot;&lt;/code&gt; and &lt;code&gt;git push&lt;/code&gt;. Open &lt;a target=&quot;_blank&quot; href=&quot;http://127.0.0.1:4546/repo1&quot;&gt;http://127.0.0.1:4546/repo1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When you are satisfied, create a branch with your CSS in &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/resources/styles/&quot;&gt;apex&lt;/a&gt;, then &lt;code&gt;git add .&lt;/code&gt; and &lt;code&gt;git push&lt;/code&gt;. I&amp;rsquo;ll take care of the rest.&lt;/p&gt;

&lt;h3 id=&quot;i-want-to-code&quot;&gt;I want to code&lt;/h3&gt;

&lt;p&gt;Okay, no worries, here&amp;rsquo;s a list of small tasks (well, they seem small to me, but if that&amp;rsquo;s not the case for you, let me know):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;in golang:

&lt;ul&gt;
&lt;li&gt;in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/grafter/&quot;&gt;grafter&lt;/a&gt; plugin, be able to display &lt;a href=&quot;issues/3d33-grafter-dco.html&quot;&gt;something else&lt;/a&gt; in the first message of a graft. The banner is hard-coded in the plugin; it would need to be exported to the plugin configuration and used.&lt;/li&gt;
&lt;li&gt;In the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/silo/&quot;&gt;silo&lt;/a&gt; plugin, allow &lt;a href=&quot;issues/76ec-silo-more-selects.html&quot;&gt;grouping by file or date&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;More difficult, but I would like to have more &lt;a href=&quot;issues/e6d8-apex-alert-support.html&quot;&gt;renderers&lt;/a&gt; in &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/apex/&quot;&gt;apex&lt;/a&gt;, for example info, warnings, and others&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;in typescript:

&lt;ul&gt;
&lt;li&gt;in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; plugin there is a bug in the RSS feed &lt;a href=&quot;issues/3986-pollen-rss-grow-infinite.html&quot;&gt;it is never cleaned up&lt;/a&gt;. Added an option (or not, 100 seems fine to me) to limit the number of &lt;code&gt;&amp;lt;item&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Still in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; plugin, why not add a &lt;a target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/JSON_Feed&quot;&gt;json feed&lt;/a&gt; in addition to the RSS feed? We can serialize json in assemblescript.&lt;/li&gt;
&lt;li&gt;More difficult, but it would be nice to be able to tell the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/pollen/&quot;&gt;pollen&lt;/a&gt; plugin to generate an RSS feed with the contents of the files. This would allow GitRoot to have an RSS feed displaying blog articles, for example. Currently, it only puts commits in the RSS feed.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;in Rust:

&lt;ul&gt;
&lt;li&gt;Do what Ygor said in &lt;a target=&quot;_blank&quot; href=&quot;https://floss.social/@ygor/115474535138702961&quot;&gt;https://floss.social/@ygor/115474535138702961&lt;/a&gt; I&amp;rsquo;m not a Rust developer and converting &lt;code&gt;String&lt;/code&gt; to &lt;code&gt;&amp;amp;str&lt;/code&gt; doesn&amp;rsquo;t mean much to me, but if you are, I&amp;rsquo;m sure it&amp;rsquo;s a small task. The code can be found in the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; and the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/plugins/hop/&quot;&gt;hop&lt;/a&gt; plugin.&lt;/li&gt;
&lt;li&gt;Add a &lt;a href=&quot; issues/9f00-rust-sdk-fake-server-test.html&quot;&gt;fakeServer&lt;/a&gt; to the &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/rust/plugin/&quot;&gt;plugin sdk&lt;/a&gt; to facilitate plugin testing. Here is the one for Go &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&quot;&gt;https://gitroot.dev/worktree/app/libs/golang/plugin/test/fakeserver.go.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;i-m-a-sysadmin&quot;&gt;I&amp;rsquo;m a sysadmin.&lt;/h3&gt;

&lt;p&gt;Perfect, there&amp;rsquo;s plenty to do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Today, there are too many steps to release GitRoot. &lt;a href=&quot;issues/dba0-simplify-release.html&quot;&gt;Automating&lt;/a&gt; as much as possible would help me a lot.&lt;/li&gt;
&lt;li&gt;If you have a little space on one of your servers, &lt;a href=&quot;doc/tutorials/deploy_instance.html&quot;&gt;try deploying an instance&lt;/a&gt; just for fun. Maybe you can improve the documentation or find something that no longer works. Or add a paragraph on using gitroot with nginx or something else.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;i-want-to-test&quot;&gt;I want to test&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;All tests are in bash in &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev/worktree/app/testsuite/&quot;&gt;/app/testsuite/&lt;/a&gt;. The largest test is located in &lt;code&gt;runtest.sh&lt;/code&gt;, along with others.

&lt;ul&gt;
&lt;li&gt;Running them to see if they work on your system seems like a good first task to me.&lt;/li&gt;
&lt;li&gt;Creating new ones&lt;/li&gt;
&lt;li&gt;Improving existing ones by, for example, moving helper functions such as &lt;code&gt;quiet_git()&lt;/code&gt;, &lt;code&gt;report()&lt;/code&gt;, or &lt;code&gt;mySleep()&lt;/code&gt; to other files (and importing them) would be a great help to the project. You can also create new ones for &lt;code&gt;killGitroot()&lt;/code&gt;, which is done at the beginning of each file. Or &lt;code&gt;createProject()&lt;/code&gt;, which consists of going to the root repository each time and adding its name to the &lt;code&gt;.gitroot/repositories.yml&lt;/code&gt; file&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;i-just-want-to-think-discuss&quot;&gt;I just want to think/discuss&lt;/h3&gt;

&lt;p&gt;You are welcome to join the &lt;a target=&quot;_blank&quot; href=&quot;https://matrix.to/#/%23gitroot:matrix.org&quot;&gt;gitroot channel&lt;/a&gt; on Matrix. Or contact us by email at &lt;code&gt;contact [AT] gitroot.dev&lt;/code&gt; or on the Fediverse at &lt;code&gt;@forge@gitroot.dev&lt;/code&gt; &lt;a target=&quot;_blank&quot; href=&quot;https://gts.gitroot.dev/@forge&quot;&gt;on this server&lt;/a&gt;.&lt;/p&gt;
</description><guid>https://gitroot.dev//blog/how-to-help_en_11-2025.md</guid></item>
<item><author>rmaneschi@gmail.com (Romain Maneschi)</author><link>https://gitroot.dev//blog/issues.md</link><title>Issues, Tickets, Discussions... Call them what you want!</title><pubDate>2026/06/08</pubDate><description>&lt;h1 id=&quot;issues-tickets-discussions-call-them-what-you-want&quot;&gt;Issues, Tickets, Discussions&amp;hellip; Call them what you want!&lt;/h1&gt;

&lt;p&gt;Many of you, when checking out &lt;a target=&quot;_blank&quot; href=&quot;https://gitroot.dev&quot;&gt;https://gitroot.dev&lt;/a&gt;, have told me that the &lt;code&gt;🐞 Issues&lt;/code&gt; button shouldn&amp;rsquo;t be labeled &amp;ldquo;Issues&amp;rdquo;, but rather &amp;ldquo;Tickets&amp;rdquo; or &amp;ldquo;Discussions&amp;rdquo;.&lt;/p&gt;

&lt;p&gt;First of all, thanks for the feedback! I really appreciate it, that&amp;rsquo;s how I can build a better forge. Of course, I will change the label, but developing a forge means having a lot of different areas to work on: issues/boards, git, code, CI/CD, and with GitRoot, even more, like plugins and WASM&amp;hellip;&lt;/p&gt;

&lt;p&gt;So, why don&amp;rsquo;t I just change this label right away? In reality, this label isn&amp;rsquo;t hardcoded inside GitRoot. It&amp;rsquo;s only there because I configured plugins to display it. I want to take this opportunity to explain how GitRoot works, because the platform is developed around this exact concept: how to customize a forge at the project level.&lt;/p&gt;

&lt;h2 id=&quot;the-original-idea&quot;&gt;The Original Idea&lt;/h2&gt;

&lt;p&gt;Imagine you are developing a forge, and to avoid vendor lock-in, you decide not to put anything outside of Git. You choose not to use any database at all. In this context, how do you design an &amp;ldquo;issue&amp;rdquo; system?&lt;/p&gt;

&lt;p&gt;In traditional forges, an &amp;ldquo;issue&amp;rdquo; consists of a title and a description created by a user. We can represent it like this:&lt;/p&gt;
&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;379.68002&quot; height=&quot;332.2&quot; viewBox=&quot;0 0 379.68002 332.2&quot;&gt;&lt;rect x=&quot;0&quot; y=&quot;0&quot; width=&quot;379.68002&quot; height=&quot;332.2&quot; fill=&quot;transparent&quot;/&gt;&lt;defs&gt;&lt;marker id=&quot;arrow-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 10 5 L 0 10 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-start-0&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;4.5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 5 L 10 10 L 10 0 z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-open-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;1&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1 7 L 18 13 V 1 Z&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-open-start-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;18&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1 7 L 18 13 V 1 Z&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-dep-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;13&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 18 7 L 9 13 L 14 7 L 9 1 Z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;marker id=&quot;arrow-class-dep-start-0&quot; viewBox=&quot;0 0 20 14&quot; refX=&quot;6&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;20&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 5 7 L 9 13 L 1 7 L 9 1 Z&quot; fill=&quot;#64748B&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot; stroke-dasharray=&quot;1,0&quot;/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;path id=&quot;edge-0&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-0&quot; d=&quot;M 135.960,121.250 L 96.540,175.250&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot; x=&quot;85.37&quot; y=&quot;134.25&quot; width=&quot;61.76&quot; height=&quot;28.00&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.85&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.35&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-0&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;116.25&quot; y=&quot;151.75&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;116.25&quot; dy=&quot;0.00&quot;&gt;Author&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;path id=&quot;edge-1&quot; class=&quot;edgePath&quot; data-edge-id=&quot;edge-1&quot; d=&quot;M 231.960,121.250 L 255.380,175.250&quot; fill=&quot;none&quot; stroke=&quot;#64748B&quot; stroke-width=&quot;1&quot;    stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; /&gt;&lt;rect data-edge-id=&quot;edge-1&quot; data-label-kind=&quot;center&quot; x=&quot;217.27&quot; y=&quot;134.25&quot; width=&quot;52.80&quot; height=&quot;28.00&quot; rx=&quot;2&quot; ry=&quot;2&quot; fill=&quot;#FFFFFF&quot; fill-opacity=&quot;0.85&quot; stroke=&quot;#94A3B8&quot; stroke-opacity=&quot;0.35&quot; stroke-width=&quot;0.8&quot;/&gt;&lt;g class=&quot;edgeLabel&quot; data-edge-id=&quot;edge-1&quot; data-label-kind=&quot;center&quot;&gt;&lt;text x=&quot;243.67&quot; y=&quot;151.75&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;243.67&quot; dy=&quot;0.00&quot;&gt;State&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;rect x=&quot;87.90&quot; y=&quot;8.00&quot; width=&quot;192.12&quot; height=&quot;113.25&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;line x1=&quot;93.90&quot; y1=&quot;46.95&quot; x2=&quot;274.02&quot; y2=&quot;46.95&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1.0&quot;/&gt;&lt;text x=&quot;183.96&quot; y=&quot;34.00&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;183.96&quot; dy=&quot;0.00&quot; font-weight=&quot;600&quot;&gt;Issue&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;117.90&quot; y=&quot;69.70&quot; text-anchor=&quot;start&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;117.90&quot; dy=&quot;0.00&quot;&gt;int id&lt;/tspan&gt;&lt;tspan x=&quot;117.90&quot; dy=&quot;17.85&quot;&gt;string title&lt;/tspan&gt;&lt;tspan x=&quot;117.90&quot; dy=&quot;17.85&quot;&gt;string description&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;203.08&quot; y=&quot;175.25&quot; width=&quot;168.60&quot; height=&quot;148.95&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;line x1=&quot;209.08&quot; y1=&quot;214.20&quot; x2=&quot;365.68&quot; y2=&quot;214.20&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1.0&quot;/&gt;&lt;text x=&quot;287.38&quot; y=&quot;201.25&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;287.38&quot; dy=&quot;0.00&quot; font-weight=&quot;600&quot;&gt;Status&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;233.08&quot; y=&quot;236.95&quot; text-anchor=&quot;start&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;0.00&quot;&gt;&amp;lt;&amp;lt;enumeration&amp;gt;&amp;gt;&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;Open&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;TODO&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;DOING&lt;/tspan&gt;&lt;tspan x=&quot;233.08&quot; dy=&quot;17.85&quot;&gt;CLOSE&lt;/tspan&gt;&lt;/text&gt;&lt;rect x=&quot;8.00&quot; y=&quot;175.25&quot; width=&quot;145.08&quot; height=&quot;95.40&quot; rx=&quot;3&quot; ry=&quot;3&quot; fill=&quot;#F8FAFC&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1&quot; stroke-linejoin=&quot;round&quot; stroke-linecap=&quot;round&quot;/&gt;&lt;line x1=&quot;14.00&quot; y1=&quot;214.20&quot; x2=&quot;147.08&quot; y2=&quot;214.20&quot; stroke=&quot;#94A3B8&quot; stroke-width=&quot;1.0&quot;/&gt;&lt;text x=&quot;80.54&quot; y=&quot;201.25&quot; text-anchor=&quot;middle&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;80.54&quot; dy=&quot;0.00&quot; font-weight=&quot;600&quot;&gt;User&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;38.00&quot; y=&quot;236.95&quot; text-anchor=&quot;start&quot; font-family=&quot;Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif&quot; font-size=&quot;14&quot; fill=&quot;#0F172A&quot;&gt;&lt;tspan x=&quot;38.00&quot; dy=&quot;0.00&quot;&gt;int id&lt;/tspan&gt;&lt;tspan x=&quot;38.00&quot; dy=&quot;17.85&quot;&gt;string email&lt;/tspan&gt;&lt;/text&gt;&lt;/svg&gt;
&lt;p&gt;Easy, right? Well, how do you represent that in GitRoot using only files? This question was running through my mind for months before I finally found a solution.&lt;/p&gt;

&lt;h2 id=&quot;the-basic-solution&quot;&gt;The Basic Solution&lt;/h2&gt;

&lt;p&gt;Since I only have files at my disposal, what actually constitutes an &amp;ldquo;issue&amp;rdquo;? I need to add some metadata to a file so the system understands that this file is an issue and not, say, a &lt;code&gt;main.go&lt;/code&gt;. I’ll probably want to group all these &amp;ldquo;issues&amp;rdquo; into a specific directory, because while GitRoot technically lets you do whatever you want (even dropping an issue right in the middle of your source code), I personally prefer to keep my files organized!&lt;/p&gt;

&lt;p&gt;A simple text format that handles metadata perfectly is Markdown. I use Markdown here, but any other markup language could work. Let&amp;rsquo;s create a file named &lt;code&gt;issues/1.md&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;id: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;user: email@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;status: OPEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# My title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt;9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;My description where I can detail what my problem is.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yes, it really is that simple. In GitRoot, to create an issue, you just create a file, add some metadata, commit, and push. Boom, you have an issue.&lt;/p&gt;

&lt;p&gt;Now, you might be thinking: &amp;ldquo;Well, I lose a lot of control with that, the status could be anything!&amp;rdquo; or &amp;ldquo;How are other users supposed to know what to put where?&amp;rdquo;. You&amp;rsquo;re right, and that is exactly where plugins enter the game.&lt;/p&gt;

&lt;h2 id=&quot;a-better-solution&quot;&gt;A Better Solution&lt;/h2&gt;

&lt;p&gt;If I simplify this as much as possible, what does an external contributor actually need to provide to create an issue? Just a title and a description. So, let&amp;rsquo;s create a file named &lt;code&gt;issues/my_issue.md&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# My title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;My description where I can detail what my problem is.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we run &lt;code&gt;git commit &amp;amp;&amp;amp; git push&lt;/code&gt;. Since I have configured the &lt;a href=&quot;plugins/name/ladybug.html&quot;&gt;ladybug plugin&lt;/a&gt; like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: ladybug
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: issues/**/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;func&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: issues/**/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;callfunc&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;default&lt;/span&gt;: autogenerated
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;mandatory&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: id
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;type&lt;/span&gt;: crc16
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;default&lt;/span&gt;: OPEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;mandatory&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: status
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;type&lt;/span&gt;: string
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;default&lt;/span&gt;: email@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;mandatory&lt;/span&gt;: &lt;span style=&quot;color:#ff79c6&quot;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: user
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;type&lt;/span&gt;: string
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can read this configuration as: &amp;ldquo;For every Markdown file in the &lt;code&gt;issues&lt;/code&gt; directory across all branches, if the file doesn&amp;rsquo;t have these metadata fields, automatically add these default values.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, if you are a Git purist, a server creating commits right on top of yours might sound a bit radical, or even terrifying. But look closely at the &lt;code&gt;write&lt;/code&gt; block in the YAML configuration above:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: issues/**/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;GitRoot doesn&amp;rsquo;t just let plugins run wild, the project configuration must explicitly grant the plugin permission to write or modify files on specific paths. If you don&amp;rsquo;t declare these rights, GitRoot will block the plugin from making any changes.&lt;/p&gt;

&lt;p&gt;When granted, GitRoot intercepts your push, lets the plugin inject the missing metadata, and safely generates a follow-up commit. And don&amp;rsquo;t worry about your history: if you decide to &lt;code&gt;git rebase -i&lt;/code&gt; later to squash or fixup your commits, plugins won&amp;rsquo;t generate duplicate commits if no metadata has changed.&lt;/p&gt;

&lt;p&gt;What about security or external users trying to forge metadata? Since contributors cannot push directly to the &lt;a href=&quot;doc/technicals/default_branch.html&quot;&gt;default branch&lt;/a&gt; (like &lt;code&gt;main&lt;/code&gt;), they have to go through a branch and need to be merged. This means every single change to an issue&amp;rsquo;s status or metadata is subjected to a code review.&lt;/p&gt;

&lt;p&gt;If an external user tries to close someone else&amp;rsquo;s issue or manipulate IDs, it will stand out like a sore thumb in the git diff. Maintainers retain absolute control and can simply reject the rogue contribution. Security doesn&amp;rsquo;t rely on complex database permission tables, but on the battle-tested git review workflow you already know and trust.&lt;/p&gt;

&lt;p&gt;So, once you push, the server-side plugin does its job, and the next time you run &lt;code&gt;git pull&lt;/code&gt;, your local file will automatically look like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;id: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;user: email@email.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;status: OPEN
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# My title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt;9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;My description where I can detail what my problem is.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bingo! You get the exact same file, with the exact same metadata you wanted. Of course, a lot more needs to be added, like defining a &lt;code&gt;type: user&lt;/code&gt; to assign an issue, or perhaps a &lt;code&gt;type: status&lt;/code&gt; to define an enum of allowed values. But I think you see where I&amp;rsquo;m going with this feature!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: If you&amp;rsquo;re wondering about the barrier to entry for non-technical contributors, my long-term vision is to allow plugins to intercept standard HTTP requests. Tomorrow, a classic web form will be able to POST to GitRoot and automatically commit a &lt;code&gt;.md&lt;/code&gt; file under the hood. But for now, we are proudly dev-centric!)&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;so-what-about-the-label&quot;&gt;So, What About the Label?&lt;/h2&gt;

&lt;p&gt;Throughout all of this, you’ll notice that the term &amp;ldquo;issue&amp;rdquo; only appears within the plugins themselves. Nowhere have we talked about that &lt;code&gt;🐞 Issues&lt;/code&gt; button yet. We actually need to introduce another plugin before we can tackle that topic.&lt;/p&gt;

&lt;p&gt;First, we need a file that lists all of these &amp;ldquo;issues&amp;rdquo;. That’s exactly why the &lt;a href=&quot;plugins/name/silo.html&quot;&gt;silo plugin&lt;/a&gt; exists. It inspects all files and groups them whenever it finds a matching pattern.&lt;/p&gt;

&lt;p&gt;To list all open &amp;ldquo;issues&amp;rdquo;, we configure it like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: silo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;func&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: boards/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - append
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;callfunc&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;boards&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;description&lt;/span&gt;: All open issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;for&lt;/span&gt;: issues/*.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;format&lt;/span&gt;: table
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;paginator&lt;/span&gt;: &lt;span style=&quot;color:#bd93f9&quot;&gt;50&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;selects&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;status: (.*)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;user: (.*)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;sort&lt;/span&gt;: select[0]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;sortOrder&lt;/span&gt;: desc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;tableHeader&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;| | status | user |&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;title&lt;/span&gt;: All issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;to&lt;/span&gt;: boards/issues.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;where&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;status: OPEN&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can read this configuration as: &amp;ldquo;For every file in the main branch, create a board called &amp;lsquo;All issues&amp;rsquo; containing links to all files matching &lt;code&gt;status: OPEN&lt;/code&gt;. Display them as a table with &lt;code&gt;status&lt;/code&gt; and &lt;code&gt;user&lt;/code&gt; columns.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After running &lt;code&gt;git commit &amp;amp;&amp;amp; git push &amp;amp;&amp;amp; git pull&lt;/code&gt;, you will get a new file at &lt;code&gt;boards/issues.md&lt;/code&gt; containing something like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;# All issues
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;font-weight:bold&quot;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;All open issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;|                                   | status | user            |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;| --------------------------------- | ------ | --------------- |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;| [&lt;span style=&quot;color:#ff79c6&quot;&gt;My title&lt;/span&gt;](&lt;span style=&quot;color:#50fa7b&quot;&gt;../issues/my_issue.md&lt;/span&gt;) | OPEN   | email@email.com |
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Silo has aggregated all your open issues into a single dashboard. Of course, you could easily create another board for closed issues in a separate file like &lt;code&gt;boards/closed.md&lt;/code&gt;, it&amp;rsquo;s entirely up to you.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: If you are sweating at the thought of a plugin scanning your entire repository on every single push, don&amp;rsquo;t panic. &lt;code&gt;silo&lt;/code&gt;, thanks to the plugin-sdk API, doesn&amp;rsquo;t do a full disk scan. It only processes the incremental diffs generated by the incoming commit. This is precisely why we restrict its scope to the &lt;code&gt;main&lt;/code&gt; branch in this example.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now that we have a page representing all our issues, we can finally render it in the browser. Because, as you might have guessed, the label causing all this debate lives in the Web UI.&lt;/p&gt;

&lt;h2 id=&quot;the-web-is-where-it-happens&quot;&gt;The Web is Where It Happens&lt;/h2&gt;

&lt;p&gt;To render my &lt;code&gt;boards/issues.md&lt;/code&gt; file on the web, I use the &lt;a href=&quot;plugins/name/apex.html&quot;&gt;apex&lt;/a&gt; plugin, in conjunction with the &lt;a href=&quot;plugins/name/apex_markdown.html&quot;&gt;apex_markdown&lt;/a&gt; plugin, of course.&lt;/p&gt;

&lt;p&gt;These plugins take files from git and render them directly in the Web UI. I configure them like this:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot; style=&quot;color:#f8f8f2;background-color:#282a36;&quot;&gt;&lt;code&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L1&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L1&quot;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;- &lt;span style=&quot;color:#ff79c6&quot;&gt;name&lt;/span&gt;: apex
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L2&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L2&quot;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L3&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L3&quot;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&quot;color:#ff79c6&quot;&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L4&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L4&quot;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L5&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L5&quot;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;branch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L6&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L6&quot;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L7&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L7&quot;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;when&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L8&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L8&quot;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L9&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L9&quot;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L10&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L10&quot;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L11&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L11&quot;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;func&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L12&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L12&quot;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;write&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L13&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L13&quot;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;git&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L14&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L14&quot;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L15&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L15&quot;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L16&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L16&quot;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L17&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L17&quot;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L18&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L18&quot;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L19&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L19&quot;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - append
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L20&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L20&quot;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;web&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L21&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L21&quot;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;path&lt;/span&gt;: &lt;span style=&quot;color:#f1fa8c&quot;&gt;&amp;#34;**/*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L22&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L22&quot;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;can&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L23&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L23&quot;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - add
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L24&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L24&quot;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - mod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L25&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L25&quot;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - del
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L26&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L26&quot;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        - append
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L27&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L27&quot;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;exec&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L28&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L28&quot;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;callfunc&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L29&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L29&quot;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;pluginname&lt;/span&gt;: apex_markdown
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L30&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L30&quot;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;funcname&lt;/span&gt;: renderMd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L31&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L31&quot;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L32&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L32&quot;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&quot;color:#ff79c6&quot;&gt;configuration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L33&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L33&quot;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L34&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L34&quot;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      &lt;span style=&quot;color:#ff79c6&quot;&gt;menu&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L35&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L35&quot;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 🏠 Home
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L36&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L36&quot;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L37&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L37&quot;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 📖 Documentation
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L38&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L38&quot;&gt;38&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /doc/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L39&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L39&quot;&gt;39&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 🔖 Versions
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L40&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L40&quot;&gt;40&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /CHANGELOG.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L41&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L41&quot;&gt;41&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: �� Plugins
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L42&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L42&quot;&gt;42&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /plugins/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L43&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L43&quot;&gt;43&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: �� Issues
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L44&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L44&quot;&gt;44&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /boards/issues.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L45&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L45&quot;&gt;45&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: 🚀 Code
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L46&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L46&quot;&gt;46&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /worktree/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L47&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L47&quot;&gt;47&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: ☕ Blog
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L48&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L48&quot;&gt;48&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /blog/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L49&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L49&quot;&gt;49&lt;/a&gt;&lt;/span&gt;&lt;span&gt;      - &lt;span style=&quot;color:#ff79c6&quot;&gt;display&lt;/span&gt;: �� Contact
&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;display:flex;&quot;&gt;&lt;span style=&quot;white-space:pre;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&quot; id=&quot;L50&quot;&gt;&lt;a style=&quot;outline: none; text-decoration:none; color:inherit&quot; href=&quot;#L50&quot;&gt;50&lt;/a&gt;&lt;/span&gt;&lt;span&gt;        &lt;span style=&quot;color:#ff79c6&quot;&gt;link&lt;/span&gt;: /contact.html
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oh, do you see it now? Yes, right there: &lt;code&gt;display: 🐞 Issues&lt;/code&gt;. This is the exact configuration that tells Apex to add a global menu item with the &lt;code&gt;🐞 Issues&lt;/code&gt; label, pointing to the &lt;code&gt;/boards/issues.html&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;If you are wondering how Apex magically knows where to find your &amp;ldquo;issues&amp;rdquo;, the secret is: it doesn&amp;rsquo;t.&lt;/p&gt;

&lt;p&gt;Apex isn&amp;rsquo;t a smart router, it&amp;rsquo;s just a file translator. It blindly takes the Markdown structure generated by GitRoot and renders it into HTML. Because I know Apex transforms &lt;code&gt;boards/issues.md&lt;/code&gt; into &lt;code&gt;boards/issues.html&lt;/code&gt;, I simply hardcode that target URL in my menu configuration (&lt;code&gt;link: /boards/issues.html&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;And what about the links inside the table? Apex has a clever built-in rewrite engine. When it parses your generated board file, it automatically rewrites every internal Markdown link, turning &lt;code&gt;[My title](../issues/my_issue.md)&lt;/code&gt; into a standard web-friendly &lt;code&gt;&amp;lt;a href=&amp;quot;../issues/my_issue.html&amp;quot;&amp;gt;&lt;/code&gt;. No database, no dynamic routing, just plain old static files mapped one-to-one.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;If you change the label to &lt;code&gt;🐞 Tickets&lt;/code&gt; or &lt;code&gt;💡 Discussions&lt;/code&gt;, the &amp;ldquo;issue to rename issues into something else&amp;rdquo; can finally be closed. Ideally, you might also want to rename some directories in the other plugins&amp;rsquo; configurations, but it&amp;rsquo;s really not a big deal.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;m pretty sure you now understand why I didn&amp;rsquo;t immediately jump on resolving this task: everything we just saw is pure configuration, completely separated from the GitRoot core code.&lt;/p&gt;

&lt;p&gt;And to be perfectly honest, I haven&amp;rsquo;t changed it yet because none of the traditional options really appeal to me. I don&amp;rsquo;t particularly like &amp;ldquo;Tickets&amp;rdquo; or &amp;ldquo;Discussions&amp;rdquo;, maybe it should be &amp;ldquo;Feature Requests&amp;rdquo; instead (and yes, you can now model that in GitRoot too!).&lt;/p&gt;

&lt;p&gt;In fact, I personally call them &lt;code&gt;TODO&lt;/code&gt;s, because in reality, they are all just waiting for me to get them done.&lt;/p&gt;

&lt;h3 id=&quot;wait-why-stop-at-issues&quot;&gt;Wait&amp;hellip; Why stop at issues?&lt;/h3&gt;

&lt;p&gt;This is where the true power of GitRoot will blow your mind.&lt;/p&gt;

&lt;p&gt;Because GitRoot doesn&amp;rsquo;t care about databases, and because everything is just a file inside Git, the &lt;code&gt;silo&lt;/code&gt; plugin can aggregate anything. I don&amp;rsquo;t need a separate &lt;code&gt;TODO&lt;/code&gt; page for my tickets, because I already have one that automatically tracks the &lt;code&gt;//TODO&lt;/code&gt; comments inside my actual source code.&lt;/p&gt;

&lt;p&gt;Yes, you read that right. You can visit it live at &lt;a href=&quot;boards/todos.html&quot;&gt;https://gitroot.dev/boards/todos.html&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Every time I write a &lt;code&gt;//TODO&lt;/code&gt; in a &lt;code&gt;.go&lt;/code&gt;, &lt;code&gt;.rs&lt;/code&gt; or &lt;code&gt;.ts&lt;/code&gt; file, GitRoot parses it, aggregates it, and generates a dynamic dashboard. Tickets, documentation, source code comments&amp;hellip; it&amp;rsquo;s all the exact same data structure.&lt;/p&gt;

&lt;p&gt;To be clear, it doesn’t magically transform your source code comments into full-blown database-like issues with IDs and statuses. It simply extracts them to give you a centralized, living view of your technical debt, right alongside your official tickets. It’s the exact same data aggregation pipeline, tailored for two different use cases.&lt;/p&gt;

&lt;hr&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to discover more plugins and configurations, feel free to browse through the &lt;a href=&quot;plugins&quot;&gt;🧩 Plugins section&lt;/a&gt;. However, for now, the best way to explore remains checking out the actual &lt;a href=&quot;.gitroot/plugins.yml&quot;&gt;GitRoot plugins.yml configuration file&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
</description><guid>https://gitroot.dev//blog/issues.md</guid></item>
  </channel>
</rss>