<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Cli on Matteo ZINUTTI</title><link>https://mzinutti.fr/en/tags/cli/</link><description>Recent content in Cli on Matteo ZINUTTI</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Mon, 12 Oct 2026 18:43:52 +0200</lastBuildDate><atom:link href="https://mzinutti.fr/en/tags/cli/index.xml" rel="self" type="application/rss+xml"/><item><title>Golang CLI</title><link>https://mzinutti.fr/en/posts/go-cli/</link><pubDate>Wed, 10 Apr 2024 22:56:48 +0000</pubDate><guid>https://mzinutti.fr/en/posts/go-cli/</guid><description>&lt;img src="https://mzinutti.fr/posts/go-cli/cover.png" alt="Featured image of post Golang CLI" /&gt;&lt;p&gt;This project was done during my first year of Master&amp;rsquo;s at Sup De Vinci, as part of a module on the Go language.&lt;/p&gt;
&lt;h2 id="introduction"&gt;Introduction
&lt;/h2&gt;&lt;p&gt;The specifications provided by the lecturer asked for a Golang CLI for one of the following three imposed authentication solutions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://casdoor.org/" target="_blank" rel="noopener"
 &gt;Casdoor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://pocketbase.io/" target="_blank" rel="noopener"
 &gt;PocketBase&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://goauthentik.io/" target="_blank" rel="noopener"
 &gt;Authentik&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Documentation had to be provided, so that the project would be turnkey and usable by anyone.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="choosing-the-solutions"&gt;Choosing the solutions
&lt;/h2&gt;&lt;h3 id="backend"&gt;Backend
&lt;/h3&gt;&lt;p&gt;After comparing the 3 authentication solutions, I noticed that Authentik and Casdoor are very complete, so they take some time to get to grips with. Given the project deadline and its needs, I went with &lt;strong&gt;PocketBase&lt;/strong&gt;. It is simple to deploy and use, and it meets exactly the requirements of the project.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://mzinutti.fr/posts/go-cli/pocketbase.png"
 alt="PocketBase home page with the admin interface listing the records of the posts collection" width="800px"&gt;&lt;figcaption&gt;
 &lt;h4&gt;PocketBase UI&lt;/h4&gt;
 &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id="golang-framework"&gt;Golang framework
&lt;/h3&gt;&lt;p&gt;For the Go framework, I saw that 2 frameworks are widely favoured in this area: &lt;a class="link" href="https://github.com/spf13/cobra" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Cobra&lt;/strong&gt; 🐍&lt;/a&gt; and &lt;a class="link" href="https://github.com/alecthomas/kong" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Kong&lt;/strong&gt; 🦍&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;By following YouTube tutorials that used the &lt;strong&gt;Cobra&lt;/strong&gt; framework, its ease of use let me quickly build a POC of my CLI with very intuitive flag handling and automated documentation. So I stuck with this framework. A huge number of projects use Cobra, as you can see in this &lt;a class="link" href="https://github.com/spf13/cobra/blob/main/site/content/projects_using_cobra.md" target="_blank" rel="noopener"
 &gt;list&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I also added the &lt;a class="link" href="https://github.com/spf13/viper" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;Viper&lt;/strong&gt;&lt;/a&gt; package to the project, which handles configuration in the Golang project, to work with the conf.json file.&lt;/p&gt;
&lt;p&gt;Finally, adding the &lt;a class="link" href="https://github.com/manifoldco/promptui" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;promptUI&lt;/strong&gt;&lt;/a&gt; package improves the UX, in particular with interactive prompts.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Interactive prompt" class="gallery-image" data-flex-basis="824px" data-flex-grow="343" height="122" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://mzinutti.fr/posts/go-cli/promptui.png" width="419"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="architecture"&gt;Architecture
&lt;/h2&gt;&lt;h3 id="cli"&gt;CLI
&lt;/h3&gt;&lt;p&gt;For the architecture of the CLI, I chose to base it on &lt;strong&gt;Docker&lt;/strong&gt;&amp;rsquo;s, which I personally find very intuitive and convenient to use, and which is also built with &lt;strong&gt;Cobra&lt;/strong&gt;. So I went with the same &lt;code&gt;command [resource] [action] [flags]&lt;/code&gt; model.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://mzinutti.fr/posts/go-cli/cli-architecture.png"
 alt="Diagram of the go-cli CLI: the root command, the user resource with the create, delete, inspect and update actions, and the version, login and logout commands" width="700px"&gt;&lt;figcaption&gt;
 &lt;h4&gt;CLI architecture&lt;/h4&gt;
 &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The CLI can therefore log in to and out of PocketBase, and perform &lt;strong&gt;CRUD&lt;/strong&gt; actions (Create, Read, Update, Delete) on the users in the backend, which meets the request in the specifications.&lt;/p&gt;
&lt;p&gt;Once the architecture of my CLI was drawn up, the design phase was over and I could start programming the CLI to make it concrete.&lt;/p&gt;
&lt;h3 id="code"&gt;Code
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── cmd
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── auth
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ ├── login.go
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ └── logout.go
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── root.go
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── user
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ ├── create.go
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ ├── delete.go
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ ├── inspect.go
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ ├── update.go
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ └── user.go
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └── version
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └── version.go
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── common
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └── common.go
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── go.mod
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── go.sum
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── go-cli
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── main.go
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;main.go&lt;/strong&gt;: initialises the program through the &lt;code&gt;main()&lt;/code&gt; function, which is the entry point of the program.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;cmd/&lt;/strong&gt;: this folder holds all the commands available in the CLI. The files are organised according to the architecture of the CLI.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;common/&lt;/strong&gt;: a module that holds the functions used in several modules of the project.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="deployment"&gt;Deployment
&lt;/h2&gt;&lt;p&gt;The deployment of the project comes down to a simple &lt;code&gt;docker-compose.yaml&lt;/code&gt; file that creates the &lt;strong&gt;PocketBase&lt;/strong&gt; container holding the &lt;strong&gt;SQLite&lt;/strong&gt; database. The pb_data file is also mounted, to have a database already populated with users.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pocketbase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;container_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;pocketbase&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="m"&gt;8090&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;8080&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./pb_data:/pb/pb_data&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The &lt;strong&gt;CLI&lt;/strong&gt; itself runs as a binary that can be downloaded from the GitLab &lt;strong&gt;releases&lt;/strong&gt; or built locally from the code.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="demo"&gt;Demo
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Demo of using the CLI" class="gallery-image" data-flex-basis="362px" data-flex-grow="151" height="397" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://mzinutti.fr/posts/go-cli/demo.gif" width="600"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;This project was quite challenging, because it came right after the one-week course module on Golang. I had to dig further into the language to handle the project properly. Having done some C in the not-so-distant past, this project brought back some good memories. 😄&lt;/p&gt;
&lt;p&gt;I also gained experience in choosing the right tools to meet the project&amp;rsquo;s expectations, and to save time on development.&lt;/p&gt;
&lt;p&gt;For more details on this project, the code is available in the &lt;a class="link" href="https://gitlab.com/Toxma/go-cli" target="_blank" rel="noopener"
 &gt;go-cli GitLab repository&lt;/a&gt;!&lt;/p&gt;</description></item></channel></rss>