<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Rustymanta - Guides</title>
    <link>https://rustymanta.xyz/guides/</link>
    <description>Article published on rustymanta.xyz</description>
    <language>en-us</language>
    <lastBuildDate>Thu, 30 Jul 2026 00:00:00 GMT</lastBuildDate>

    <atom:link
        href="https://rustymanta.xyz/guides/rss.xml"
        rel="self"
        type="application/rss+xml"/>

    
    <item>
      <title>Setting up Helix for Java Maven Projects</title>
      <link>https://rustymanta.xyz/setting-up-helix-for-java-maven-projects</link>
      <guid>https://rustymanta.xyz/setting-up-helix-for-java-maven-projects</guid>

      <description><![CDATA[
      Setting up Helix for Java Maven Projects
      ]]></description>

      <content><![CDATA[
      <p>I don't use <a href="https://www.java.com">Java</a> very often, so my tooling around that
language is fairly basic. But today I was looking at the
<a href="https://github.com/onthegomap/planetiler">Planetiler</a> project and needed proper
IDE features to explore it.</p>
<p>My editor of choice for a long time has been <a href="https://helix-editor.com/">Helix</a>.
It supports the standard Java
<a href="https://en.wikipedia.org/wiki/Language_Server_Protocol">LSP</a>
<a href="https://github.com/eclipse-jdtls/eclipse.jdt.ls">JDTLS</a> out of the box and
gives you most of the LSP features you'd expect.</p>
<p>When trying to understand any new codebase, I like to explore the actual source
code, and a very useful LSP feature for that is the
"<a href="https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_definition">Go to Definition</a>"
which lets you jump to the definition of any symbol. There are a few steps
required to make it work for a <a href="https://maven.apache.org/">Maven</a> Java project.</p>
<p>First, you need to explicitly ask Maven to download the source
<a href="https://docs.oracle.com/en/java/javase/25/docs/specs/man/jar.html">JARs</a> for
your dependencies, not just the compiled artifacts. Otherwise, there is no code
to jump to.</p>
<pre style="background-color:#292c3c;">
<span style="font-style:italic;color:#8caaee;">mvn</span><span style="color:#c6d0f5;"> dependency:sources
</span></pre>
<p>This will download the <code>*-sources.jar</code> files next to the compiled artifacts in
your local Maven repository. But, those sources are <strong>packaged as
archives</strong>. As such, JDTLS returns a special
<a href="https://en.wikipedia.org/wiki/Uniform_Resource_Identifier">URI scheme</a> <code>jdt://</code>
to the editor, rather than a link to a regular file. Helix doesn't know how to
handle that scheme and the maintainers
<a href="https://github.com/helix-editor/helix/issues/11559#issuecomment-2307936921">do not want</a>
to add support for any nonstandard LSP extensions.</p>
<p>Fortunately, <a href="https://quantonganh.com/">Quan Tong</a> wrote a tiny
<a href="https://github.com/quantonganh/jdtls-wrapper">LSP wrapper</a> for exactly this use
case in 2025 (see also his
<a href="https://quantonganh.com/posts/2025/08/14/jdtls-wrapper/">blog post</a> where he
goes into detail on how he debugged it). Following the documentation, I
installed it and configured it in Helix's <code>language.toml</code> as follows:</p>
<pre style="background-color:#292c3c;">
<span style="color:#949cbb;">[[</span><span style="color:#81c8be;">language</span><span style="color:#949cbb;">]]
</span><span style="color:#8caaee;">name </span><span style="color:#949cbb;">= </span><span style="color:#a6d189;">&quot;java&quot;
</span><span style="color:#8caaee;">language-servers </span><span style="color:#949cbb;">= [ </span><span style="color:#a6d189;">&quot;jdtls&quot; </span><span style="color:#949cbb;">]
</span><span style="color:#8caaee;">indent </span><span style="color:#949cbb;">= { </span><span style="color:#8caaee;">tab-width </span><span style="color:#949cbb;">= </span><span style="color:#ef9f76;">4</span><span style="color:#949cbb;">, </span><span style="color:#8caaee;">unit </span><span style="color:#949cbb;">= </span><span style="color:#a6d189;">&quot;    &quot; </span><span style="color:#949cbb;">}
</span><span style="color:#c6d0f5;">
</span><span style="color:#949cbb;">[</span><span style="color:#81c8be;">language-server</span><span style="color:#949cbb;">.</span><span style="color:#81c8be;">jdtls</span><span style="color:#949cbb;">]
</span><span style="color:#8caaee;">command </span><span style="color:#949cbb;">= </span><span style="color:#a6d189;">&quot;jdtls-wrapper&quot;
</span><span style="color:#c6d0f5;">
</span><span style="color:#949cbb;">[</span><span style="color:#81c8be;">language-server</span><span style="color:#949cbb;">.</span><span style="color:#81c8be;">jdtls</span><span style="color:#949cbb;">.</span><span style="color:#81c8be;">config</span><span style="color:#949cbb;">]
</span><span style="color:#8caaee;">java</span><span style="color:#949cbb;">.</span><span style="color:#8caaee;">inlayHints</span><span style="color:#949cbb;">.</span><span style="color:#8caaee;">parameterNames</span><span style="color:#949cbb;">.</span><span style="color:#8caaee;">enabled </span><span style="color:#949cbb;">= </span><span style="color:#a6d189;">&quot;all&quot;
</span><span style="color:#8caaee;">extendedClientCapabilities</span><span style="color:#949cbb;">.</span><span style="color:#8caaee;">classFileContentsSupport </span><span style="color:#949cbb;">= </span><span style="color:#e78284;">true
</span></pre>
<p>And it works just fine. I can finally jump around all that Java code.</p>

      ]]></content>

      <author>arman@rustymanta.xyz</author>

      <pubDate>Thu, 30 Jul 2026 00:00:00 GMT</pubDate>
    </item>
    
    <item>
      <title>Git Is All You Need</title>
      <link>https://rustymanta.xyz/git-is-all-you-need</link>
      <guid>https://rustymanta.xyz/git-is-all-you-need</guid>

      <description><![CDATA[
      A short guide to hosting Git projects using git-http-backend and the cgit web UI.
      ]]></description>

      <content><![CDATA[
      <div class="post-intro">
This short guide will walk you through how I'm hosting my read-only public Git repos using <a href="https://git-scm.com/docs/git-http-backend">git-http-backend</a> and the <a href="https://git.zx2c4.com/cgit/about/">cgit</a> web UI.
</div>
<p><img src="/guides/images/github_error_500.webp" alt="github error 500" /></p>
<p>The final code can be found in
<a href="https://git.rustymanta.xyz/git_serve/">this repo</a> (hosted using the very method
described in this guide). Everything is deployed with Docker to make it portable
and easier to install on a new server.</p>
<h2>Git bare repo primer</h2>
<p>For those who don't know, Git has the concept of a
<a href="https://git-scm.com/book/en/v2/Git-on-the-Server-Getting-Git-on-a-Server">bare repository</a>.
This is a Git folder that does not have the working directory and only contains
Git's data. This is what you use if you want to create a Git remote repo on a
server yourself.</p>
<p>You initialize it on your server with:</p>
<pre style="background-color:#292c3c;">
<span style="font-style:italic;color:#8caaee;">git</span><span style="color:#c6d0f5;"> init</span><span style="color:#949cbb;"> --</span><span style="font-style:italic;color:#ea999c;">bare </span><span style="color:#e78284;">$</span><span style="color:#c6d0f5;">REPO_NAME.git
</span></pre>
<p>And then you can clone it through SSH with:</p>
<pre style="background-color:#292c3c;">
<span style="font-style:italic;color:#8caaee;">git</span><span style="color:#c6d0f5;"> clone </span><span style="color:#e78284;">$</span><span style="color:#c6d0f5;">SERVER_URL:</span><span style="color:#e78284;">$</span><span style="color:#c6d0f5;">REPO_PATH
</span></pre>
<p>After that, everything works the same as if you were cloning a GitHub repo. For
a private personal project it is enough to run a bare repo on a remotely
accessible server you control. This is what I use for most of my projects. The
rest of this guide will explore the case when you also want to publicly expose
some projects to anyone on the internet.</p>
<h2>Building Caddy with CGI support</h2>
<p>I'm using <a href="https://caddyserver.com/">Caddy</a> for the HTTP server mainly because
the configuration is easier than <a href="https://nginx.org/">NGINX</a> or
<a href="https://httpd.apache.org/">Apache</a>. Also, Caddy has excellent out-of-the-box
TLS termination, and most of the time it just works without any configuration.</p>
<p>Unfortunately git-http-backend and cgit communicate with the HTTP server through
the
<a href="https://en.wikipedia.org/wiki/Common_Gateway_Interface">Common Gateway Interface</a>
(CGI). It is not supported natively by Caddy, so we need to build Caddy
ourselves with the CGI extension. It is easily done with
<a href="https://github.com/caddyserver/xcaddy">xcaddy</a>.</p>
<pre style="background-color:#292c3c;">
<span style="font-style:italic;color:#8caaee;">xcaddy</span><span style="color:#c6d0f5;"> build</span><span style="color:#949cbb;"> --</span><span style="font-style:italic;color:#ea999c;">with</span><span style="color:#c6d0f5;"> github.com/aksdb/caddy-cgi/v2
</span></pre>
<h2>git-http-backend</h2>
<p>Let's start by exposing the remote repo for Git operations using the HTTP
backend. We need to make sure that we are <strong>only allowing read
operations</strong>, otherwise anyone on the internet would be able to modify
the repositories. By default, git-http-backend should disable the
<a href="https://git-scm.com/docs/git-http-backend#Documentation/git-http-backend.txt-httpreceivepack">receive-pack service</a>
(receiving modifications from clients), but on my side, I explicitly
<a href="https://git.rustymanta.xyz/git_serve/tree/caddy_config/gitconfig#n5">disable it</a>.
Also, because I am running with Docker I can
<a href="https://git.rustymanta.xyz/git_serve/tree/compose.yml#n14">mount the repo directory</a>
as a read-only directory as an additional protection.</p>
<p>Note that although we are blocking pushes through the HTTP transport we are
still able to <strong>push changes using SSH</strong>. That way you can only
modify the repos if and only if you have SSH access to the server.</p>
<p>Here is a basic <a href="https://caddyserver.com/docs/caddyfile">Caddyfile</a> config that
routes all traffic from the Git User-Agent to the git-http-backend process
through CGI.</p>
<pre style="background-color:#292c3c;">
<span style="color:#c6d0f5;">git.rustymanta.xyz</span><span style="color:#949cbb;"> {
</span><span style="color:#c6d0f5;">  </span><span style="color:#81c8be;">@git</span><span style="color:#949cbb;"> {
</span><span style="color:#c6d0f5;">    </span><span style="color:#ca9ee6;">header</span><span style="color:#c6d0f5;"> User-Agent git/*
</span><span style="color:#949cbb;">  }
</span><span style="color:#c6d0f5;">
</span><span style="color:#c6d0f5;">  </span><span style="font-style:italic;color:#8caaee;">route</span><span style="color:#949cbb;"> {
</span><span style="color:#c6d0f5;">    </span><span style="color:#ca9ee6;">handle</span><span style="color:#c6d0f5;"> @git</span><span style="color:#949cbb;"> {
</span><span style="color:#c6d0f5;">      </span><span style="color:#ca9ee6;">cgi</span><span style="color:#c6d0f5;"> * /usr/libexec/git-core/git-http-backend</span><span style="color:#949cbb;"> {
</span><span style="color:#c6d0f5;">        </span><span style="color:#ca9ee6;">env</span><span style="color:#c6d0f5;"> GIT_PROJECT_ROOT=/git-repos GIT_HTTP_EXPORT_ALL=1 GIT_HTTP_RECEIVEPACK=0
</span><span style="color:#949cbb;">      }
</span><span style="color:#949cbb;">    }
</span><span style="color:#949cbb;">  }
</span><span style="color:#949cbb;">}
</span></pre>
<p>And this is all you need for allowing <code>git clone</code> with HTTP. You could also
double-check that doing <code>git push</code> to this remote will lead to a <code>403</code> error.
Next, let's expose a nice HTTP UI.</p>
<h2>Cgit</h2>
<p>Cgit is technically not needed. If you want to keep it minimal, you could, for
example, just have an HTML page with the cloning URL for your repos. But it is a
nice addition, allowing people to browse the code without cloning it. It's also
pretty handy when you want to link a commit or file in a discussion.</p>
<p>Like git-http-backend, cgit communicates through CGI. The Caddy route is pretty
similar to the previous one, we need one route for the static cgit files (like
the CSS file) and one for the dynamic content generated from the Git repos.</p>
<pre style="background-color:#292c3c;">
<span style="color:#c6d0f5;">handle /cgit.*</span><span style="color:#949cbb;"> {
</span><span style="color:#c6d0f5;">    </span><span style="font-style:italic;color:#8caaee;">root</span><span style="color:#c6d0f5;"> * /usr/share/webapps/cgit
</span><span style="color:#c6d0f5;">    </span><span style="font-style:italic;color:#8caaee;">file_server
</span><span style="color:#949cbb;">}
</span><span style="color:#c6d0f5;">
</span><span style="color:#c6d0f5;">handle</span><span style="color:#949cbb;"> {
</span><span style="color:#c6d0f5;">    </span><span style="font-style:italic;color:#8caaee;">cgi</span><span style="color:#c6d0f5;"> * /usr/share/webapps/cgit/cgit.cgi</span><span style="color:#949cbb;"> {
</span><span style="color:#c6d0f5;">        </span><span style="color:#ca9ee6;">env</span><span style="color:#c6d0f5;"> CGIT_CONFIG=/cgitrc
</span><span style="color:#949cbb;">    }
</span><span style="color:#949cbb;">}
</span></pre>
<h2>Cgit configuration</h2>
<p>I did not spend too much time digging into the
<a href="https://man.archlinux.org/man/cgitrc.5">cgit configuration</a> options, you can
refer to the
<a href="https://git.rustymanta.xyz/git_serve/tree/cgitrc">latest version of my config</a>
for an up-to-date, complete version.</p>
<p>I chose not to have cgit auto-discovery the repos and instead add them manually
in the <code>cgitrc</code> file. For example, here is the config for a single repo.</p>
<pre style="background-color:#292c3c;">
<span style="color:#8caaee;">repo</span><span style="color:#949cbb;">.</span><span style="color:#8caaee;">url</span><span style="color:#949cbb;">=</span><span style="color:#c6d0f5;">git_serve
</span><span style="color:#8caaee;">repo</span><span style="color:#949cbb;">.</span><span style="color:#8caaee;">name</span><span style="color:#949cbb;">=</span><span style="color:#c6d0f5;">Git Serve
</span><span style="color:#8caaee;">repo</span><span style="color:#949cbb;">.</span><span style="color:#8caaee;">path</span><span style="color:#949cbb;">=</span><span style="color:#c6d0f5;">/git-repos/git_serve.git
</span><span style="color:#8caaee;">repo</span><span style="color:#949cbb;">.</span><span style="color:#8caaee;">desc</span><span style="color:#949cbb;">=</span><span style="color:#c6d0f5;">Read only dockerized way of sharing some git repos
</span><span style="color:#8caaee;">repo</span><span style="color:#949cbb;">.</span><span style="color:#8caaee;">clone-url</span><span style="color:#949cbb;">=</span><span style="color:#c6d0f5;">https://git.rustymanta.xyz/git_serve
</span></pre>
<p>In addition to the basic config I added a
<a href="https://git.rustymanta.xyz/git_serve/tree/cgit-email-filter.sh">simple email obfuscation script</a>,
configured by setting the <code>email-filter</code> variable in
<a href="https://git.rustymanta.xyz/git_serve/tree/cgitrc#n37">cgitrc</a>. Cgit is calling
it with the original email in <code>stdin</code> and expects the filtered email in
<code>stdout</code>.</p>
<pre style="background-color:#292c3c;">
<span style="color:#c6d0f5;">IFS</span><span style="color:#81c8be;">= </span><span style="font-style:italic;color:#8caaee;">read </span><span style="color:#949cbb;">-</span><span style="font-style:italic;color:#ea999c;">r</span><span style="color:#c6d0f5;"> email
</span><span style="font-style:italic;color:#8caaee;">printf </span><span style="color:#a6d189;">&#39;%s\n&#39; &quot;</span><span style="color:#e78284;">$</span><span style="color:#c6d0f5;">email</span><span style="color:#a6d189;">&quot; </span><span style="color:#949cbb;">\
</span><span style="color:#c6d0f5;">    </span><span style="color:#81c8be;">| </span><span style="font-style:italic;color:#8caaee;">sed </span><span style="color:#949cbb;">\
</span><span style="color:#949cbb;">        -</span><span style="font-style:italic;color:#ea999c;">e </span><span style="color:#a6d189;">&#39;s/@/ (at) /&#39; </span><span style="color:#949cbb;">\
</span><span style="color:#949cbb;">        -</span><span style="font-style:italic;color:#ea999c;">e </span><span style="color:#a6d189;">&#39;s/\./ (dot) /g&#39;
</span></pre>
<p>For the styling I based mine on the <a href="https://catppuccin.com">Catppuccin theme</a>
port created by <a href="https://vidhukant.com/blog/cgit-catppuccin/">VidhuKant</a>. The
README in <a href="https://git.vidhukant.com/cgit-catppuccin/about/">VidhuKant's repo</a>
also explains how to set up the markdown and code highlighting.</p>
<h2>Workflow</h2>
<p>This is most of the infra work for hosting public repos. How you use it is
mostly up to you, but here is my personal workflow for reference. I use two
remotely accessible computers with bare Git repos on them.</p>
<ul>
<li>A private one that I use to sync pretty much all my code-related projects and
share code between my computers (accessible only via SSH in my
<a href="https://www.wireguard.com/">WireGuard</a> VPN).</li>
<li>And a public one running the Caddy server.</li>
</ul>
<p>For most of my projects, my <code>origin</code>
<a href="https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes">remote</a> points
to my <strong>private remote</strong> and, <strong>only if</strong> I want to
publish the code on the internet do I create a new bare repo on the public
server and add a <code>public</code> remote to the same project. That way I have full
control over when and what I publish.</p>
<pre style="background-color:#292c3c;">
<span style="color:#81c8be;">&gt;&gt;&gt;</span><span style="color:#c6d0f5;"> git remote</span><span style="color:#949cbb;"> -</span><span style="font-style:italic;color:#ea999c;">v
</span><span style="font-style:italic;color:#8caaee;">origin</span><span style="color:#c6d0f5;">  cupcake:~/Repos/git_serve (fetch)
</span><span style="font-style:italic;color:#8caaee;">origin</span><span style="color:#c6d0f5;">  cupcake:~/Repos/git_serve (push)
</span><span style="font-style:italic;color:#8caaee;">public</span><span style="color:#c6d0f5;">  eclair:~/PublicRepos/git_serve (fetch)
</span><span style="font-style:italic;color:#8caaee;">public</span><span style="color:#c6d0f5;">  eclair:~/PublicRepos/git_serve (push)
</span></pre>
<img style="width: 600px" src="/guides/diagrams/git_workflow.svg" alt="git workflow diagram">
<p>So far I'm pretty happy with that workflow.</p>
<h2>No collaboration?</h2>
<p>In that setup, there is no collaboration features or contribution workflow. I am
the only one that can push modifications to the repos. That being said, other
people could always discuss and send me code patches through email, and that
might be enough for most of the projects.</p>
<p>I plan on exploring collaboration infra in the future.</p>

      ]]></content>

      <author>arman@rustymanta.xyz</author>

      <pubDate>Sun, 26 Jul 2026 00:00:00 GMT</pubDate>
    </item>
    

  </channel>
</rss>
