<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Nicolas Martyanoff – Brain dump</title>
    <description>Brain dump</description>
    <language>en-us</language>
    <managingEditor>nicolas@n16f.net (Nicolas Martyanoff)</managingEditor>
    <webMaster>nicolas@n16f.net (Nicolas Martyanoff)</webMaster>
    <link>https://www.n16f.net/tags/golang/index.xml</link>
    <atom:link href="https://www.n16f.net/tags/golang/index.xml" rel="self" type="application/rss+xml" />
    <lastBuildDate>Sun, 07 Jul 2024 00:00:00 +0000</lastBuildDate>

    
    <item xml:base="https://www.n16f.net/blog/taking-control-of-your-go-module-paths/">
      <title>Taking control of your Go module paths</title>
      <link>https://www.n16f.net/blog/taking-control-of-your-go-module-paths/</link>
      <pubDate>Sun, 07 Jul 2024 18:00:00 +0000</pubDate>
      <guid isPermaLink="true">https://www.n16f.net/blog/taking-control-of-your-go-module-paths/</guid>
      <author>nicolas@n16f.net (Nicolas Martyanoff)</author>

      <description>&lt;p&gt;The canonical name of a Go module is its path. For third party modules, that
means an URI. It is no secret that GitHub is currently the dominant code hosting
platform, hence the fact that most Go modules are named
&lt;code&gt;github.com/&amp;lt;account&amp;gt;/&amp;lt;repository&amp;gt;&lt;/code&gt;. While I currently use GitHub for most of my
repositories, I also want the ability to move out of it in the future.
Fortunately the Go module system allows package authors to control how the
package is accessed. One can use it to provide Go modules with their own domain
and total control on module paths. Let us see how to do that.&lt;/p&gt;
&lt;h2 id=&#34;how-packages-are-downloaded&#34;&gt;How packages are downloaded&lt;/h2&gt;
&lt;p&gt;When the Go client downloads a module (either directly with &lt;code&gt;GOPROXY=direct&lt;/code&gt; or
when a proxy does it), it fetches the module URI with an HTTP request and a
&lt;code&gt;go-get=1&lt;/code&gt; query parameter. The server includes a &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; HTML element named
&lt;code&gt;go-import&lt;/code&gt; in the response, its value indicating how to actually download the
module.&lt;/p&gt;
&lt;p&gt;Let us check ourselves with my &lt;code&gt;go-uuid&lt;/code&gt; module:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ curl -s https://github.com/galdor/go-uuid\?go-get\=1 | grep go-import
  &amp;lt;meta name=&amp;quot;go-import&amp;quot; content=&amp;quot;github.com/galdor/go-uuid git https://github.com/galdor/go-uuid.git&amp;quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;GitHub indicates that to download the &lt;code&gt;github.com/galdor/go-uuid&lt;/code&gt; module, the
client must clone the Git repository at &lt;a href=&#34;https://github.com/galdor/go-uuid.git&#34;&gt;https://github.com/galdor/go-uuid.git&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So for a module to be named &lt;code&gt;example.com/foo&lt;/code&gt;, all you need is an HTTP server
responding to requests at &lt;code&gt;https://example.com/foo?go-get=1&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;using-github-pages&#34;&gt;Using GitHub Pages&lt;/h2&gt;
&lt;p&gt;I wanted all my packages to be available at &lt;code&gt;go.n16f.net/&amp;lt;name&amp;gt;&lt;/code&gt;. It is short,
it looks good, and it opens the possibility to move the repositories out of
GitHub in the future.&lt;/p&gt;
&lt;p&gt;While I could have used my own HTTP server, I was worried about what would
happen if someone needed to download a module when the server is down. Since I
already use GitHub to host most repositories, I decided to use GitHub Pages:
after all, what I need is a way to host HTML pages with the right &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tag
at the right URI.&lt;/p&gt;
&lt;h3 id=&#34;generating-repository-pages&#34;&gt;Generating repository pages&lt;/h3&gt;
&lt;p&gt;I created a &lt;a href=&#34;https://github.com/galdor/go.n16f.net&#34;&gt;GitHub repository&lt;/a&gt; for these
pages. It was tempting to use a simple shell script to generate pages but I
really did not want to use
&lt;a href=&#34;https://en.wikipedia.org/wiki/M4_%28computer_language%29&#34;&gt;M4&lt;/a&gt; and Ruby lets me use
&lt;a href=&#34;https://github.com/ruby/erb&#34;&gt;ERB&lt;/a&gt; templates, so Ruby it is.&lt;/p&gt;
&lt;p&gt;All my Go modules are listed in a &lt;a href=&#34;https://github.com/galdor/go.n16f.net/blob/master/modules.txt&#34;&gt;text
file&lt;/a&gt;, and a
small &lt;a href=&#34;https://github.com/galdor/go.n16f.net/blob/master/generate-pages&#34;&gt;Ruby
script&lt;/a&gt;
generates both an index (not required but why not?) and a page for each module.
The &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tag of each module page indicates that &lt;code&gt;go.n16f.net/&amp;lt;name&amp;gt;&lt;/code&gt; can be
accessed by cloning the Git repository at &lt;code&gt;https://github.com/galdor/go-&amp;lt;name&amp;gt;&lt;/code&gt;.
It is that simple.&lt;/p&gt;
&lt;h3 id=&#34;configuring-the-domain&#34;&gt;Configuring the domain&lt;/h3&gt;
&lt;p&gt;Using your own domain means configuring it to point to GitHub pages.&lt;/p&gt;
&lt;p&gt;First head to your domain name registrar website and configure your domain to
point to GitHub pages. In my case, that means creating a &lt;code&gt;CNAME&lt;/code&gt; DNS record
named &lt;code&gt;go.n16f.net.&lt;/code&gt; pointing to &lt;code&gt;galdor.github.io.&lt;/code&gt;. You will notice that the
GitHub Pages address is global to your account and not specific to each
repository: GitHub takes care of routing, based on which domain is associated
with each repository.&lt;/p&gt;
&lt;p&gt;Then go to the GitHub Pages &lt;a href=&#34;https://github.com/settings/pages&#34;&gt;settings page&lt;/a&gt;
and add your domain. You will have to create a &lt;code&gt;TXT&lt;/code&gt; DNS record with a specific
name and value to prove ownership of the domain.&lt;/p&gt;
&lt;p&gt;Once done, go to the GitHub Pages settings page of the repository. Select the
&amp;ldquo;GitHub Actions&amp;rdquo; source, and add the domain to &amp;ldquo;Custom domain&amp;rdquo;. Make sure to
tick &amp;ldquo;Enforce HTTPS&amp;rdquo; while you are here.&lt;/p&gt;
&lt;h3 id=&#34;publishing-pages&#34;&gt;Publishing pages&lt;/h3&gt;
&lt;p&gt;To generate and publish pages, we use a small GitHub Actions
&lt;a href=&#34;https://github.com/galdor/go.n16f.net/blob/master/.github/workflows/generate-pages.yaml&#34;&gt;workflow&lt;/a&gt;.
When new commits are pushed to the &lt;code&gt;master&lt;/code&gt; branch, we run the script (the
&lt;code&gt;ubuntu-latest&lt;/code&gt; image already contains Ruby, so there is nothing to setup) and
use official GitHub actions to upload and deploy the pages we just generated.&lt;/p&gt;
&lt;p&gt;A quick check shows that the new URIs work as expected:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ curl -s https://go.n16f.net/uuid\?go-get\=1 | grep go-import
    &amp;lt;meta name=&amp;quot;go-import&amp;quot; content=&amp;quot;go.n16f.net/uuid git https://github.com/galdor/go-uuid.git&amp;quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;updating-modules&#34;&gt;Updating modules&lt;/h2&gt;
&lt;p&gt;You can now update your Go modules: update the path in the &lt;code&gt;go.mod&lt;/code&gt; file, and
all import paths in source files. Yes this is annoying, and I would love a way
to declare the path of each dependencies at project level, but
&lt;a href=&#34;https://en.wikipedia.org/wiki/Sed&#34;&gt;Sed&lt;/a&gt; makes the task trivial.&lt;/p&gt;
&lt;p&gt;Updating dependencies is a bit trickier. If your project depends on
&lt;code&gt;github.com/example/foo&lt;/code&gt; which you just renamed to &lt;code&gt;example.com/foo&lt;/code&gt;, update all
module paths in source files then run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GOPROXY=direct go get example.com/foo@latest &amp;amp;&amp;amp; go mod tidy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will force Go to fetch the module directly from its source and not from the
global proxy which may not be up-to-date yet.&lt;/p&gt;
&lt;h2 id=&#34;result&#34;&gt;Result&lt;/h2&gt;
&lt;p&gt;For a surprisingly small amount of work, I now have all my Go modules available
at a path I fully control and I can add new modules by modifying a text file and
letting the GitHub workflow handles the rest. And while the current setup is
still dependent on GitHub, I can move it somewhere else without any change to
module paths.&lt;/p&gt;
&lt;p&gt;What is your excuse for not doing the same thing?&lt;/p&gt;
</description>

      
      <category>golang</category>
      
    </item>
    
    <item xml:base="https://www.n16f.net/blog/golang-workspaces-at-last/">
      <title>Golang workspaces, at last!</title>
      <link>https://www.n16f.net/blog/golang-workspaces-at-last/</link>
      <pubDate>Tue, 28 Mar 2023 18:00:00 +0000</pubDate>
      <guid isPermaLink="true">https://www.n16f.net/blog/golang-workspaces-at-last/</guid>
      <author>nicolas@n16f.net (Nicolas Martyanoff)</author>

      <description>&lt;p&gt;Workspaces are a feature introduced in March 2022 with Go 1.18. They did not
really get a lot of publicity, and I have not had the chance to experiment
with them until recently. However I am really glad I did because they improve
a major aspect of my workflow: dealing with multiple modules.&lt;/p&gt;
&lt;h2 id=&#34;go-modules-are-not-ideal&#34;&gt;Go modules are not ideal&lt;/h2&gt;
&lt;p&gt;Go modules were a big help when they were introduced in 2018, but they were
always limited. Larger products are often split into multiple projects: one or
more applications and several libraries and tools, meaning multiple modules.&lt;/p&gt;
&lt;p&gt;While module versioning makes sure that components use the right version of
each dependency, it is also really annoying during development. If your
application &lt;code&gt;foo&lt;/code&gt; depends on the &lt;code&gt;go-bar&lt;/code&gt; library, you will often have to work
on &lt;code&gt;go-bar&lt;/code&gt; while testing the changes in &lt;code&gt;foo&lt;/code&gt;. This means updating &lt;code&gt;go-bar&lt;/code&gt;,
commiting changes, pushing them, and updating the &lt;code&gt;foo&lt;/code&gt; module with &lt;code&gt;go get&lt;/code&gt;.
Quite cumbersome.&lt;/p&gt;
&lt;p&gt;A first improvement is the module replacement system. In the example above, we
could instruct Go to use the local copy of &lt;code&gt;go-bar&lt;/code&gt; when building &lt;code&gt;foo&lt;/code&gt;.
Assuming that &lt;code&gt;foo&lt;/code&gt; and &lt;code&gt;go-bar&lt;/code&gt; are at the same level in the filesystem:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;go mod edit -replace example.com/myproject/go-bar=../go-bar
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this simple change, you have made your life easier: you can work on
changes in &lt;code&gt;go-bar&lt;/code&gt;, and immediately build and run &lt;code&gt;foo&lt;/code&gt; without any
additional operations. When you are done, you can still commit and push
normally in &lt;code&gt;go-bar&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But it is still not perfect. Module replacements are stored in the &lt;code&gt;go.mod&lt;/code&gt;
file, meaning that these changes will be pushed to your central repository
circumventing dependency versioning and causing issues for other developers
and CI processes. While module replacement works fine to point a module to a
fork, it is not really a solution for our problem.&lt;/p&gt;
&lt;h2 id=&#34;using-workspaces&#34;&gt;Using workspaces&lt;/h2&gt;
&lt;p&gt;Go workspaces let you create environments where you control the source of the
modules you use, without having to modify these modules.&lt;/p&gt;
&lt;p&gt;Going back to our example, we can solve our problem by creating a workspace
for &lt;code&gt;foo&lt;/code&gt; in which &lt;code&gt;go-bar&lt;/code&gt; refers to the local copy. In the directory of &lt;code&gt;foo&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;go work init
go work use .
go work use ../go-bar
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nothing complicated here. The &lt;code&gt;init&lt;/code&gt; subcommand creates the &lt;code&gt;go.work&lt;/code&gt; file
which will contain the configuration of the workspace. Then the &lt;code&gt;use&lt;/code&gt;
subcommand is called to add two modules to the workspace: the &lt;code&gt;foo&lt;/code&gt; module in
the current repository, i.e. &lt;code&gt;.&lt;/code&gt;, and the one in the &lt;code&gt;go-bar&lt;/code&gt; sibling directory.&lt;/p&gt;
&lt;p&gt;At this point, building &lt;code&gt;foo&lt;/code&gt; will correctly use the local copy of &lt;code&gt;go-bar&lt;/code&gt;
without having to modify any of the modules. Problem solved.&lt;/p&gt;
&lt;p&gt;Even better, you can include &lt;code&gt;replace&lt;/code&gt; declarations in the &lt;code&gt;go.work&lt;/code&gt; file the
exact same way as in &lt;code&gt;go.mod&lt;/code&gt; file. These declarations will override those in
module files, giving you total control on the environment, again without
having to alter modules.&lt;/p&gt;
&lt;h2 id=&#34;committing-the-workspace-file&#34;&gt;Committing the workspace file&lt;/h2&gt;
&lt;p&gt;Whether you commit the workspace file or not depends on your situation. When
working in a mono-repository with other developers, committing a workspace
file allows everyone to build the project the exact same way, using
dependencies in the repository. It can also be practical with multiple
repositories as long as you expect everyone to organize their local copies the
same way.&lt;/p&gt;
&lt;p&gt;But you can also keep your own workspace files without committing them. This
gives you the ability to quickly switch to a local copy for a dependency or to
replace a module by another one during development. For example this what I do
for my &lt;a href=&#34;https://github.com/galdor/go-raft/&#34;&gt;go-raft library&lt;/a&gt; project. The
program in the &lt;code&gt;cmd/kvstore&lt;/code&gt; directory is based on the &lt;a href=&#34;https://github.com/galdor/go-service&#34;&gt;go-service
library&lt;/a&gt;. During development, I
sometimes have to add code to go-service. Therefore I have a workspace file in
go-raft which references the local copy of go-service. But I do not commit it,
to avoid affecting anyone trying to build go-raft.&lt;/p&gt;
&lt;p&gt;This flexibility is really practical, and I&amp;rsquo;m quite satisfied with workspaces
at this point.&lt;/p&gt;
&lt;h2 id=&#34;what-could-be-better&#34;&gt;What could be better&lt;/h2&gt;
&lt;p&gt;There is a small issue to keep in mind. If you get used to work with
workspaces, committing your local dependencies and having your program use
them automatically, you may still have to maintain module dependencies. It is
not necessarily a problem if you commit the workspace file and always use it,
but it makes sense to keep dependencies up-to-date in your module files.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;go work&lt;/code&gt; command has a &lt;code&gt;sync&lt;/code&gt; subcommand whose description let me think
that it would update modules included in the workspace to the right dependency
versions, but it turns out not do to so when tracking
&lt;a href=&#34;https://go.dev/ref/mod#pseudo-versions&#34;&gt;pseudo-versions&lt;/a&gt; (i.e. when you are
tracking a branch and not a specific tag).&lt;/p&gt;
&lt;p&gt;Therefore I have to continue to update non-tagged dependencies with this usual
(and quite excessive) command, here for go-service:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GOPROXY=direct go get github.com/galdor/go-service@latest
go mod tidy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This way I make sure to bypass the &lt;a href=&#34;https://proxy.golang.org/&#34;&gt;Go proxy&lt;/a&gt;,
fetch the last version of the &lt;code&gt;master&lt;/code&gt; branch and clean up the dependency
list.&lt;/p&gt;
&lt;p&gt;Despite this small inconvenience, Go workspaces have made my daily work much
easier. Still a win!&lt;/p&gt;
</description>

      
      <category>golang</category>
      
    </item>
    
  </channel>
</rss>
