<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tidal</title>
    <description>The latest articles on DEV Community by Tidal (@tidalcloud).</description>
    <link>https://dev.to/tidalcloud</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F3743%2F4cbc1a0b-d6c8-4a64-af36-7afb1db8098c.png</url>
      <title>DEV Community: Tidal</title>
      <link>https://dev.to/tidalcloud</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tidalcloud"/>
    <language>en</language>
    <item>
      <title>Interactive CLI prompts in Go</title>
      <dc:creator>Petr Razumov</dc:creator>
      <pubDate>Wed, 25 Aug 2021 18:24:42 +0000</pubDate>
      <link>https://dev.to/tidalcloud/interactive-cli-prompts-in-go-3bj9</link>
      <guid>https://dev.to/tidalcloud/interactive-cli-prompts-in-go-3bj9</guid>
      <description>&lt;h2&gt;
  
  
  Tidal Migrations 💓 CLI applications
&lt;/h2&gt;

&lt;p&gt;Do you like CLI applications? We love them! At Tidal Migrations we use full-featured GUI IDEs and editors like VS Code and Emacs but also &lt;code&gt;vim&lt;/code&gt; and &lt;code&gt;git&lt;/code&gt; running in our terminals. Every day we use &lt;code&gt;bash&lt;/code&gt;, &lt;code&gt;awk&lt;/code&gt;, &lt;code&gt;sed&lt;/code&gt; and lots of other CLI tools and apps for work and fun. Also, we like to develop &lt;a href="https://get.tidal.sh/" rel="noopener noreferrer"&gt;CLI apps&lt;/a&gt; and with this post, we're going to show you how to implement different interactive prompts for your CLI apps written in Go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Passing data to CLI apps
&lt;/h2&gt;

&lt;p&gt;Oftentimes CLI applications don't just work by themselves, but some process or operation is required on the information or data.&lt;/p&gt;

&lt;p&gt;There are different ways to pass data to command line applications. Using flags, environment variables, file names as CLI arguments or reading from standard input is quite common and is pretty easy to implement using just the &lt;a href="https://pkg.go.dev/std" rel="noopener noreferrer"&gt;standard Go library&lt;/a&gt;. Using interactive prompts can &lt;em&gt;spice up&lt;/em&gt; your CLI application and improve the overall UX.&lt;/p&gt;

&lt;p&gt;Let's get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  How to implement text input prompt
&lt;/h2&gt;

&lt;p&gt;The basic text input prompt is easy to implement. Just read from standard input until the &lt;a href="https://en.wikipedia.org/wiki/Newline" rel="noopener noreferrer"&gt;new line character&lt;/a&gt; (&lt;code&gt;\n&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"bufio"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
    &lt;span class="s"&gt;"strings"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// StringPrompt asks for a string value using the label&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;StringPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;bufio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;'\n'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TrimSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;StringPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"What is your name?"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, %s!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;



&lt;h2&gt;
  
  
  How to implement password input prompt
&lt;/h2&gt;

&lt;p&gt;Password prompts are similar to text input prompts, except the user's typed input should be hidden:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
    &lt;span class="s"&gt;"syscall"&lt;/span&gt;

    &lt;span class="s"&gt;"golang.org/x/term"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// PasswordPrompt asks for a string value using the label.&lt;/span&gt;
&lt;span class="c"&gt;// The entered value will not be displayed on the screen&lt;/span&gt;
&lt;span class="c"&gt;// while typing.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;PasswordPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;term&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadPassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;PasswordPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"What is your password?"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Oh, I see! Your password is %q&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;



&lt;h2&gt;
  
  
  How to implement Yes/No prompt
&lt;/h2&gt;

&lt;p&gt;For Yes/No prompts we're going to create an infinite loop to keep asking until the user answers yes or no:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"bufio"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
    &lt;span class="s"&gt;"strings"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// YesNoPrompt asks yes/no questions using the label.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;YesNoPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;choices&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"Y/n"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;choices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"y/N"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;bufio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"%s (%s) "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;'\n'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TrimSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;def&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"y"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"yes"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"n"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"no"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;YesNoPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Dev.to is awesome!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Agree!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Huh?"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;



&lt;h2&gt;
  
  
  How to implement interactive checkboxes
&lt;/h2&gt;

&lt;p&gt;To create an interactive multi-select prompt we're going to use an awesome &lt;a href="https://github.com/AlecAivazis/survey" rel="noopener noreferrer"&gt;&lt;code&gt;survey&lt;/code&gt;&lt;/a&gt; package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"strings"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/AlecAivazis/survey/v2"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Checkboxes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;survey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MultiSelect&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;survey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AskOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;answers&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Checkboxes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"Which are your favourite programming languages?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"C"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Python"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Java"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"C++"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"C#"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Visual Basic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"JavaScript"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"PHP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Assembly Language"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"SQL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Groovy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Classic Visual Basic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Fortran"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"R"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Ruby"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Swift"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"MATLAB"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Go"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Prolog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Perl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;answers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;", "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Oh, I see! You like"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;



&lt;h2&gt;
  
  
  Caveats and workarounds
&lt;/h2&gt;

&lt;p&gt;If you pipe some input data to your interactive CLI app, the prompts will read that data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Petr"&lt;/span&gt; | go run main.go
&lt;span class="go"&gt;What is your name? Hello, Petr!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Sometimes such behavior is acceptable, but sometimes not. To check if the terminal is interactive let's use &lt;a href="https://pkg.go.dev/golang.org/x/term#IsTerminal" rel="noopener noreferrer"&gt;&lt;code&gt;term.IsTerminal&lt;/code&gt;&lt;/a&gt; function:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"syscall"&lt;/span&gt;

    &lt;span class="s"&gt;"golang.org/x/term"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;term&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsTerminal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Terminal is interactive! You're good to use prompts!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Terminal is not interactive! Consider using flags or environment variables!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Hello"&lt;/span&gt; | go run main.go
&lt;span class="go"&gt;Terminal is not interactive! Consider using flags or environment variables!

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;go run main.go
&lt;span class="go"&gt;Terminal is interactive! You're good to use prompts!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Libraries
&lt;/h2&gt;

&lt;p&gt;As you can see, it's pretty easy to implement basic interactive prompts, but for complex ones it's better to use some Go packages from the community:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/AlecAivazis" rel="noopener noreferrer"&gt;
        AlecAivazis
      &lt;/a&gt; / &lt;a href="https://github.com/AlecAivazis/survey" rel="noopener noreferrer"&gt;
        survey
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A golang library for building interactive and accessible prompts with full support for windows and posix terminals.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Survey&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://pkg.go.dev/github.com/AlecAivazis/survey/v2" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/8d171e8467bdec23d2d49f80fd74c5553fec95271f014e9bcba99e71753ab0b0/687474703a2f2f696d672e736869656c64732e696f2f62616467652f676f646f632d7265666572656e63652d3532373242342e737667" alt="GoDoc"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A library for building interactive and accessible prompts on terminals supporting ANSI escape sequences.&lt;/p&gt;

&lt;p&gt;⚠️ This project is no longer maintained. For an alternative, please check out: &lt;a href="https://github.com/charmbracelet/bubbletea" rel="noopener noreferrer"&gt;https://github.com/charmbracelet/bubbletea&lt;/a&gt; ⚠️&lt;/p&gt;

&lt;p&gt;Hey everyone! I finally came to terms with the fact that I can no longer dedicate enough time to keep this library alive
This project outgrew my wildest expectations and was such a great experience. If someone else wants to take over maintainence
please reach out&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/618bfd3c94f2724b3a1c4ce60e6307fcaf91914f1e07cb22c6f2b491e04fa08c/68747470733a2f2f7468756d62732e6766796361742e636f6d2f56696c6c61696e6f757347726163696f75734b6f75707265792d73697a655f726573747269637465642e676966"&gt;&lt;img width="550" src="https://camo.githubusercontent.com/618bfd3c94f2724b3a1c4ce60e6307fcaf91914f1e07cb22c6f2b491e04fa08c/68747470733a2f2f7468756d62732e6766796361742e636f6d2f56696c6c61696e6f757347726163696f75734b6f75707265792d73697a655f726573747269637465642e676966"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight highlight-source-go notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;package&lt;/span&gt; main
&lt;span class="pl-k"&gt;import&lt;/span&gt; (
    &lt;span class="pl-s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="pl-s"&gt;"github.com/AlecAivazis/survey/v2"&lt;/span&gt;
)

&lt;span class="pl-c"&gt;// the questions to ask&lt;/span&gt;
&lt;span class="pl-k"&gt;var&lt;/span&gt; &lt;span class="pl-s1"&gt;qs&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; []&lt;span class="pl-c1"&gt;*&lt;/span&gt;survey.&lt;span class="pl-smi"&gt;Question&lt;/span&gt;{
    {
        &lt;span class="pl-s1"&gt;Name&lt;/span&gt;:     &lt;span class="pl-s"&gt;"name"&lt;/span&gt;,
        &lt;span class="pl-s1"&gt;Prompt&lt;/span&gt;:   &lt;span class="pl-c1"&gt;&amp;amp;&lt;/span&gt;survey.&lt;span class="pl-smi"&gt;Input&lt;/span&gt;{&lt;span class="pl-s1"&gt;Message&lt;/span&gt;: &lt;span class="pl-s"&gt;"What is your name?"&lt;/span&gt;},
        &lt;span class="pl-s1"&gt;Validate&lt;/span&gt;: &lt;span class="pl-s1"&gt;survey&lt;/span&gt;.&lt;span class="pl-c1"&gt;Required&lt;/span&gt;,
        &lt;span class="pl-s1"&gt;Transform&lt;/span&gt;: &lt;span class="pl-s1"&gt;survey&lt;/span&gt;.&lt;span class="pl-c1"&gt;Title&lt;/span&gt;,
    },
    {
        &lt;span class="pl-s1"&gt;Name&lt;/span&gt;: &lt;span class="pl-s"&gt;"color"&lt;/span&gt;,
        &lt;span class="pl-s1"&gt;Prompt&lt;/span&gt;: &lt;span class="pl-c1"&gt;&amp;amp;&lt;/span&gt;survey.&lt;span class="pl-smi"&gt;Select&lt;/span&gt;{
            &lt;span class="pl-s1"&gt;Message&lt;/span&gt;: &lt;span class="pl-s"&gt;"Choose a color:"&lt;/span&gt;,
            &lt;span class="pl-s1"&gt;Options&lt;/span&gt;: []&lt;span class="pl-smi"&gt;string&lt;/span&gt;{&lt;span class="pl-s"&gt;"red"&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/AlecAivazis/survey" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Songmu" rel="noopener noreferrer"&gt;
        Songmu
      &lt;/a&gt; / &lt;a href="https://github.com/Songmu/prompter" rel="noopener noreferrer"&gt;
        prompter
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      golang utility for easy prompting
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;prompter&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://github.com/Songmu/prompter/actions?workflow=test" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/Songmu/prompter/workflows/test/badge.svg?branch=main" alt="Test Status"&gt;&lt;/a&gt;
&lt;a href="https://codecov.io/gh/Songmu/prompter" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b7754ff0214fdaa4ea0626c58db9371b71f81af41b639fac06990e6617e6ef1b/68747470733a2f2f636f6465636f762e696f2f67682f536f6e676d752f70726f6d707465722f6272616e63682f6d61696e2f67726170682f62616467652e737667" alt="Coverage Status"&gt;&lt;/a&gt;
&lt;a href="https://github.com/Songmu/prompter/blob/main/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/33c44e11f2026269872ef17e691c7d0712059a191121e709451737950255bbe5/687474703a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e7376673f7374796c653d666c61742d737175617265" alt="MIT License"&gt;&lt;/a&gt;
&lt;a href="http://pkg.go.dev/github.com/Songmu/prompter" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/301ec25d1b45309f89e3ee7760ca6c257077461791fd288a1c9221168b44cb00/68747470733a2f2f706b672e676f2e6465762f62616467652f6769746875622e636f6d2f536f6e676d752f70726f6d70746572" alt="GoDev"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Description&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;utility for easy prompting in Golang&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Synopsis&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-go notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-s1"&gt;twitterID&lt;/span&gt; &lt;span class="pl-c1"&gt;:=&lt;/span&gt; &lt;span class="pl-s1"&gt;prompter&lt;/span&gt;.&lt;span class="pl-c1"&gt;Prompt&lt;/span&gt;(&lt;span class="pl-s"&gt;"Enter your twitter ID"&lt;/span&gt;, &lt;span class="pl-s"&gt;""&lt;/span&gt;)
&lt;span class="pl-s1"&gt;lang&lt;/span&gt; &lt;span class="pl-c1"&gt;:=&lt;/span&gt; &lt;span class="pl-s1"&gt;prompter&lt;/span&gt;.&lt;span class="pl-c1"&gt;Choose&lt;/span&gt;(&lt;span class="pl-s"&gt;"Which language do you like the most?"&lt;/span&gt;, []&lt;span class="pl-smi"&gt;string&lt;/span&gt;{&lt;span class="pl-s"&gt;"Perl"&lt;/span&gt;, &lt;span class="pl-s"&gt;"Golang"&lt;/span&gt;, &lt;span class="pl-s"&gt;"Scala"&lt;/span&gt;, &lt;span class="pl-s"&gt;"Ruby"&lt;/span&gt;}, &lt;span class="pl-s"&gt;"Perl"&lt;/span&gt;)
&lt;span class="pl-s1"&gt;passwd&lt;/span&gt; &lt;span class="pl-c1"&gt;:=&lt;/span&gt; &lt;span class="pl-s1"&gt;prompter&lt;/span&gt;.&lt;span class="pl-c1"&gt;Password&lt;/span&gt;(&lt;span class="pl-s"&gt;"Enter your password"&lt;/span&gt;)
&lt;span class="pl-k"&gt;var&lt;/span&gt; &lt;span class="pl-s1"&gt;likeSushi&lt;/span&gt; &lt;span class="pl-smi"&gt;bool&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;prompter&lt;/span&gt;.&lt;span class="pl-c1"&gt;YN&lt;/span&gt;(&lt;span class="pl-s"&gt;"Do you like sushi?"&lt;/span&gt;, &lt;span class="pl-c1"&gt;true&lt;/span&gt;)
&lt;span class="pl-k"&gt;var&lt;/span&gt; &lt;span class="pl-s1"&gt;likeBeer&lt;/span&gt; &lt;span class="pl-smi"&gt;bool&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;prompter&lt;/span&gt;.&lt;span class="pl-c1"&gt;YesNo&lt;/span&gt;(&lt;span class="pl-s"&gt;"Do you like beer?"&lt;/span&gt;, &lt;span class="pl-c1"&gt;false&lt;/span&gt;)&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Easy to use&lt;/li&gt;
&lt;li&gt;Care non-interactive (not a tty) environment
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Default&lt;/code&gt; is used and the process is not blocked&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;No howeyc/gopass (which uses cgo) dependency
&lt;ul&gt;
&lt;li&gt;cross build friendly&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Customizable prompt setting by using &lt;code&gt;&amp;amp;prompter.Prompter{}&lt;/code&gt; directly&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;License&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://github.com/Songmu/prompter/blob/main/LICENSE" rel="noopener noreferrer"&gt;MIT&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Author&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://github.com/Songmu" rel="noopener noreferrer"&gt;Songmu&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Songmu/prompter" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;

&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/manifoldco" rel="noopener noreferrer"&gt;
        manifoldco
      &lt;/a&gt; / &lt;a href="https://github.com/manifoldco/promptui" rel="noopener noreferrer"&gt;
        promptui
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Interactive prompt for command-line applications
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;promptui&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Interactive prompt for command-line applications.&lt;/p&gt;

&lt;p&gt;We built Promptui because we wanted to make it easy and fun to explore cloud
services with &lt;a href="https://github.com/manifoldco/manifold-cli" rel="noopener noreferrer"&gt;manifold cli&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/manifoldco/promptui./CODE_OF_CONDUCT.md" rel="noopener noreferrer"&gt;Code of Conduct&lt;/a&gt; |
&lt;a href="https://github.com/manifoldco/promptui./.github/CONTRIBUTING.md" rel="noopener noreferrer"&gt;Contribution Guidelines&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/manifoldco/promptui/releases" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/3999050cf726b8c88645d4c8f05b4ce6cd72426f6a19bff14bf712ec62377885/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f7461672f6d616e69666f6c64636f2f70726f6d707475692e7376673f6c6162656c3d6c6174657374" alt="GitHub release"&gt;&lt;/a&gt;
&lt;a href="https://godoc.org/github.com/manifoldco/promptui" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/38d3a4a042b196a18998b0b4f6ab2b10a3a5428b47d96dcd51a6af884f23d4e1/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f676f646f632d7265666572656e63652d626c75652e737667" alt="GoDoc"&gt;&lt;/a&gt;
&lt;a href="https://travis-ci.org/manifoldco/promptui" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/7fd563fd590fc49a27d2906f0eda3284b900405576604eee8cb1363d6d1ce628/68747470733a2f2f696d672e736869656c64732e696f2f7472617669732f6d616e69666f6c64636f2f70726f6d707475692f6d61737465722e737667" alt="Travis"&gt;&lt;/a&gt;
&lt;a href="https://goreportcard.com/report/github.com/manifoldco/promptui" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/1c19c26096955cc3cbfe3bb701f5b73e57e75dc7da0c31f1475fa7520cd3615b/68747470733a2f2f676f7265706f7274636172642e636f6d2f62616467652f6769746875622e636f6d2f6d616e69666f6c64636f2f70726f6d70747569" alt="Go Report Card"&gt;&lt;/a&gt;
&lt;a href="https://github.com/manifoldco/promptui./LICENSE.md" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/3f9f35c866835373f2429f9db357a9b10435ebff479df3e62b494edb3a2fe933/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4253442d626c75652e737667" alt="License"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Overview&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/655695af0f159ae635d7c4d66cf53c0076282c337d4f2fa9c39741ceffbc4e7a/68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f78554e6461304e67623571736f674c7342692f67697068792e676966"&gt;&lt;img src="https://camo.githubusercontent.com/655695af0f159ae635d7c4d66cf53c0076282c337d4f2fa9c39741ceffbc4e7a/68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f78554e6461304e67623571736f674c7342692f67697068792e676966" alt="promptui"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Promptui is a library providing a simple interface to create command-line
prompts for go. It can be easily integrated into
&lt;a href="https://github.com/spf13/cobra" rel="noopener noreferrer"&gt;spf13/cobra&lt;/a&gt;
&lt;a href="https://github.com/urfave/cli" rel="noopener noreferrer"&gt;urfave/cli&lt;/a&gt; or any cli go application.&lt;/p&gt;
&lt;p&gt;Promptui has two main input modes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Prompt&lt;/code&gt; provides a single line for user input. Prompt supports
optional live validation, confirmation and masking the input.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Select&lt;/code&gt; provides a list of options to choose from. Select supports
pagination, search, detailed view and custom templates.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a full list of options check &lt;a href="https://godoc.org/github.com/manifoldco/promptui" rel="nofollow noopener noreferrer"&gt;GoDoc&lt;/a&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Basic Usage&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Prompt&lt;/h3&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-go notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;package&lt;/span&gt; main
&lt;span class="pl-k"&gt;import&lt;/span&gt; (
    &lt;span class="pl-s"&gt;"errors"&lt;/span&gt;
    &lt;span class="pl-s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="pl-s"&gt;"strconv"&lt;/span&gt;

    &lt;span class="pl-s"&gt;"github.com/manifoldco/promptui"&lt;/span&gt;
)

&lt;span class="pl-k"&gt;func&lt;/span&gt; &lt;span class="pl-s1"&gt;main&lt;/span&gt;() {
    &lt;span class="pl-s1"&gt;validate&lt;/span&gt; &lt;span class="pl-c1"&gt;:=&lt;/span&gt; &lt;span class="pl-k"&gt;func&lt;/span&gt;(&lt;span class="pl-s1"&gt;input&lt;/span&gt; &lt;span class="pl-smi"&gt;string&lt;/span&gt;) &lt;span class="pl-smi"&gt;error&lt;/span&gt; {
        &lt;span class="pl-s1"&gt;_&lt;/span&gt;, &lt;span class="pl-s1"&gt;err&lt;/span&gt; &lt;span class="pl-c1"&gt;:=&lt;/span&gt; &lt;span class="pl-s1"&gt;strconv&lt;/span&gt;.&lt;span class="pl-c1"&gt;ParseFloat&lt;/span&gt;(&lt;span class="pl-s1"&gt;input&lt;/span&gt;, &lt;span class="pl-c1"&gt;64&lt;/span&gt;)
        &lt;span class="pl-k"&gt;if&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/manifoldco/promptui" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That's it! We hope you liked it! Code examples are available on &lt;a href="https://github.com/tidalmigrations/interactive-cli-prompts" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you're interested in CLI applications development in Go and we — &lt;a href="https://tidalmigrations.com/" rel="noopener noreferrer"&gt;Tidal Migrations&lt;/a&gt; — are hiring! Please check our &lt;a href="https://tidalmigrations.com/careers/" rel="noopener noreferrer"&gt;Careers&lt;/a&gt; page!&lt;/p&gt;

&lt;p&gt;Long live the command line!&lt;/p&gt;

</description>
      <category>go</category>
      <category>cli</category>
    </item>
    <item>
      <title>Automating multi-arch container images builds (we used Google Cloud Build but GitHub Actions would also work)</title>
      <dc:creator>Petr Razumov</dc:creator>
      <pubDate>Tue, 29 Jun 2021 11:52:30 +0000</pubDate>
      <link>https://dev.to/tidalcloud/automating-multi-arch-container-images-builds-we-used-google-cloud-build-but-github-actions-would-also-work-clb</link>
      <guid>https://dev.to/tidalcloud/automating-multi-arch-container-images-builds-we-used-google-cloud-build-but-github-actions-would-also-work-clb</guid>
      <description>&lt;p&gt;This is the last post in the series on how we prepared our application to run on M1 (Apple Silicon).&lt;/p&gt;

&lt;p&gt;In the previous part we were talking about &lt;a href="https://dev.to/tidalmigrations/multi-cpu-architecture-container-images-how-to-build-and-push-them-on-docker-hub-or-any-other-registry-2981"&gt;building multiple CPU architecture container images&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post will guide you through how to implement a pipeline for building multi-architecture container images using Google Cloud Platform’s &lt;a href="https://cloud.google.com/build" rel="noopener noreferrer"&gt;Cloud Build&lt;/a&gt; or &lt;a href="https://github.com/features/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As you may know from the previous post, to build multi-arch container images we need to setup virtual environment and run &lt;code&gt;docker buildx&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's configure such build pipelines for some popular CI/CD solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloud Build
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://cloud.google.com/build" rel="noopener noreferrer"&gt;Cloud Build&lt;/a&gt; is a service that executes your builds on Google Cloud Platform's infrastructure.&lt;/p&gt;

&lt;p&gt;To run multi-arch container images builds let's use the following pipeline configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr.io/cloud-builders/docker&lt;/span&gt;
    &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;run&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--privileged'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;linuxkit/binfmt:v0.7'&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;initialize-qemu&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr.io/cloud-builders/docker&lt;/span&gt;
    &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;buildx&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;create&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--name'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mybuilder&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create-builder&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr.io/cloud-builders/docker&lt;/span&gt;
    &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;buildx&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;use&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mybuilder&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;select-builder&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr.io/cloud-builders/docker&lt;/span&gt;
    &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;buildx&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;inspect&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--bootstrap'&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;show-target-build-platforms&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr.io/cloud-builders/docker&lt;/span&gt;
    &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;buildx&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--platform'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;$_DOCKER_BUILDX_PLATFORMS&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-t'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/hello-world:latest'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--push'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build-multi-architecture-container-image&lt;/span&gt;
&lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DOCKER_CLI_EXPERIMENTAL=enabled&lt;/span&gt;
&lt;span class="na"&gt;substitutions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;_DOCKER_BUILDX_PLATFORMS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;linux/amd64,linux/arm64'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above configuration creates the pipeline to set up the build environment (&lt;code&gt;initialize-qemu&lt;/code&gt;, &lt;code&gt;create-builder&lt;/code&gt;, &lt;code&gt;select-builder&lt;/code&gt;, and &lt;code&gt;show-target-build-platforms&lt;/code&gt; steps), to build a multi-arch container image and to push it to the &lt;a href="https://cloud.google.com/container-registry" rel="noopener noreferrer"&gt;Container Registry&lt;/a&gt; on Google Cloud Platform (&lt;code&gt;build-multi-architecture-container-image&lt;/code&gt; step).&lt;/p&gt;

&lt;p&gt;To push to another container registry (e.g. Docker Hub) update the &lt;code&gt;build-multi-architecture-container-image&lt;/code&gt; step accordingly.&lt;/p&gt;

&lt;p&gt;Please consult the Cloud Build documentation to get the information on how to &lt;a href="https://cloud.google.com/build/docs/how-to#trigger" rel="noopener noreferrer"&gt;trigger builds&lt;/a&gt; (manually or automatically).&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub Actions
&lt;/h2&gt;

&lt;p&gt;If you prefer to use GitHub Actions instead of Cloud Build, it is also possible.&lt;/p&gt;

&lt;p&gt;The pipeline configuration to build multi-arch container images is the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ci&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;buildx&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;checkout code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;setup qemu&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-qemu-action@v1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;setup buildx&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;buildx&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-buildx-action@v1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;available platforms&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo ${{ steps.buildx.outputs.platforms }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;login to docker hub&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/login-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKER_USERNAME }}&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKER_TOKEN }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build the image&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;docker buildx build \&lt;/span&gt;
          &lt;span class="s"&gt;--push \&lt;/span&gt;
          &lt;span class="s"&gt;--tag ${{ secrets.DOCKER_USERNAME }}/hello-world:latest \&lt;/span&gt;
          &lt;span class="s"&gt;--platform linux/amd64,linux/arm64 . &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make it work you need to set up some &lt;a href="https://docs.github.com/en/actions/reference/encrypted-secrets" rel="noopener noreferrer"&gt;secrets on GitHub&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DOCKER_USERNAME&lt;/code&gt; — your user name on Docker Hub&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DOCKER_TOKEN&lt;/code&gt; — your Docker Hub &lt;a href="https://hub.docker.com/settings/security" rel="noopener noreferrer"&gt;access token&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That was the last post in the series, and to sum up let's repeat the key points on how to prepare an application for M1 (Apple Silicon):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use Go to easily build applications for different operating systems&lt;/li&gt;
&lt;li&gt;Build multi-arch container images with &lt;code&gt;docker buildx&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Utilize Cloud Build or GitHub Action to automate builds&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>cicd</category>
      <category>cloudbuild</category>
      <category>multiarch</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>Multi-CPU architecture container images. How to build and push them on Docker Hub (or any other registry)</title>
      <dc:creator>Petr Razumov</dc:creator>
      <pubDate>Wed, 02 Jun 2021 12:09:09 +0000</pubDate>
      <link>https://dev.to/tidalcloud/multi-cpu-architecture-container-images-how-to-build-and-push-them-on-docker-hub-or-any-other-registry-2981</link>
      <guid>https://dev.to/tidalcloud/multi-cpu-architecture-container-images-how-to-build-and-push-them-on-docker-hub-or-any-other-registry-2981</guid>
      <description>&lt;p&gt;This is the second post in the series on how we prepared our application to run on M1 (Apple Silicon).&lt;/p&gt;

&lt;p&gt;In the previous part we were talking about Go programming language and its ability to easily &lt;a href="https://dev.to/tidalmigrations/how-to-cross-compile-go-app-for-apple-silicon-m1-27l6"&gt;cross-compile applications&lt;/a&gt; for different operating systems and CPU architectures using just a developer's laptop.&lt;/p&gt;

&lt;p&gt;With this post, I'm going to describe some other aspects of modern cross-platform applications development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our application
&lt;/h2&gt;

&lt;p&gt;It's worth repeating that at &lt;a href="https://tidalmigrations.com/" rel="noopener noreferrer"&gt;Tidal Migrations&lt;/a&gt; we build our CLI application — &lt;a href="https://tidalmigrations.com/tidal-tools/" rel="noopener noreferrer"&gt;Tidal Tools&lt;/a&gt; — to make it easier for our customers to deal with all sorts of data necessary on their way towards the clouds. Tidal Migrations' &lt;a href="https://tidalmigrations.com/2021-may-newsletter/#product-highlight" rel="noopener noreferrer"&gt;May 2021 Newsletter&lt;/a&gt; describes Tidal Tools as the&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;meat-and-potatoes of how you’ll start your cloud journey.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The CLI app could be run anywhere:&lt;/p&gt;

&lt;p&gt;Locally&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;on Microsoft Windows&lt;/li&gt;
&lt;li&gt;on Apple macOS (Intel or M1)&lt;/li&gt;
&lt;li&gt;on GNU/Linux&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or preinstalled on a free cloud VM in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/marketplace/pp/prodview-uicif637zwja2" rel="noopener noreferrer"&gt;AWS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://azuremarketplace.microsoft.com/en-us/marketplace/apps/tidal-migrations.tidal_tools" rel="noopener noreferrer"&gt;Azure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://console.cloud.google.com/marketplace/product/tidal-migrations-public/tidal-tools" rel="noopener noreferrer"&gt;Google Cloud Platform&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tidal Tools architecture in brief
&lt;/h2&gt;

&lt;p&gt;Tidal Tools is a Go command-line interface (CLI) application. It mostly acts as a &lt;a href="https://guides.tidalmg.com/api-getting-started.html" rel="noopener noreferrer"&gt;Tidal Migrations API&lt;/a&gt; client accompanying our &lt;a href="https://get.tidalmg.com/" rel="noopener noreferrer"&gt;web application&lt;/a&gt;. It also has some additional features like &lt;a href="https://guides.tidalmg.com/analyze-source-code.html" rel="noopener noreferrer"&gt;source code&lt;/a&gt; and &lt;a href="https://guides.tidalmg.com/analyze-database.html" rel="noopener noreferrer"&gt;database analysis&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Those two extra functionalities are implemented by &lt;a href="https://tidalmigrations.com/technology-partners/" rel="noopener noreferrer"&gt;our technological partners&lt;/a&gt; in other programming languages. &lt;/p&gt;

&lt;p&gt;We build Docker container images for such 3rd-party solutions and our application (Tidal Tools) runs containers under the hood using the awesome &lt;a href="https://pkg.go.dev/github.com/docker/docker/client" rel="noopener noreferrer"&gt;Docker Go SDK&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problems with container images
&lt;/h2&gt;

&lt;p&gt;While preparing a new release of Tidal Tools for M1 Macs we discovered that our existing Docker container images won't work on the new Apple Silicon architecture. After some investigation we figured out that we build our container images for &lt;code&gt;amd64&lt;/code&gt; architecture, while M1 Macs expect images for &lt;code&gt;arm64&lt;/code&gt; CPU architecture.&lt;/p&gt;

&lt;p&gt;Docker images can support multiple architectures, which means that a single image may contain variants for different architectures, and sometimes for different operating systems, such as Windows.&lt;/p&gt;

&lt;p&gt;When running an image with multi-architecture support, &lt;code&gt;docker&lt;/code&gt; automatically selects the image variant that matches your OS and architecture.&lt;/p&gt;

&lt;p&gt;After some trial and error with our Docker images we are now finally confident in our happy path on how to build multi-CPU architecture Docker container images. In other words, we now know how to build container images for different architectures and push such images to container registries (e.g. Docker Hub) to be used on machines with different OSes and architectures. In short, build on (for example) Debian GNU/Linux — run on (for example) macOS for M1!&lt;/p&gt;

&lt;p&gt;So, without further delay, let's jump straight to the topic!&lt;/p&gt;

&lt;h2&gt;
  
  
  How to build multi-arch container images with &lt;code&gt;docker buildx&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CAUTION!&lt;/strong&gt; To build container images with multi-CPU architecture support, you need to use &lt;a href="https://docs.docker.com/glossary/#parent-image" rel="noopener noreferrer"&gt;parent image&lt;/a&gt; which supports multiple CPU architectures. Most of the official images on Docker Hub provide a &lt;a href="https://github.com/docker-library/official-images#architectures-other-than-amd64" rel="noopener noreferrer"&gt;variety of architectures&lt;/a&gt;. For example, the &lt;code&gt;openjdk&lt;/code&gt; image variants (which we're going to use later) support &lt;code&gt;arm64v8&lt;/code&gt; and &lt;code&gt;amd64&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For this example, we're going to use “Hello world” application written in Java:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// HelloWorld.java&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloWorld&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello world!"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's start with a &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Dockerfile&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;openjdk:8-jdk-slim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; HelloWorld.java /app/&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;javac HelloWorld.java

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; openjdk:8-jre-slim&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/HelloWorld.class /app/&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["java", "HelloWorld"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make it possible to build multi-CPU architecture container images we need to install &lt;a href="https://github.com/docker/buildx/" rel="noopener noreferrer"&gt;Docker Buildx&lt;/a&gt;. Docker Buildx is a CLI plugin that extends the &lt;code&gt;docker&lt;/code&gt; command with some additional features, and multi-arch builds is one of those. If you're using recent Docker Desktop or Docker for Linux packages chances are high that Buildx is already available for you. If not, check the &lt;a href="https://github.com/docker/buildx/#installing" rel="noopener noreferrer"&gt;installation instructions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create and switch to using a new builder which gives access to the new multi-architecture features:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker buildx create --name mybuilder --use
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Log in to a Docker registry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build and push multi-arch container image for x86-64 (&lt;code&gt;amd64&lt;/code&gt;) and AArch64 (&lt;code&gt;arm64&lt;/code&gt;) CPU platforms (replace &lt;code&gt;your-username&lt;/code&gt; with the actual Docker registry user name):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker buildx build . \
  --platform linux/arm64,linux/amd64 \
  --tag your-username/hello-world:latest \
  --push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the above commands would build and push multi-arch container images to your Docker Hub profile:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frqe289cmmn6fwj13qtn2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frqe289cmmn6fwj13qtn2.png" alt="alt text" width="800" height="168"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;When running such image with multi-architecture support, &lt;code&gt;docker&lt;/code&gt; automatically selects the image variant that matches the running OS and architecture.&lt;/p&gt;

&lt;p&gt;That's it! With this simple trick you can build Docker container images for different operating systems and architectures and host such images on Docker Hub.&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this post! Stay tuned to learn more about  how we prepared our application to run on M1 (Apple Silicon)!&lt;/p&gt;

</description>
      <category>docker</category>
      <category>multiarch</category>
      <category>containers</category>
    </item>
    <item>
      <title>How to cross-compile Go app for Apple Silicon (M1)</title>
      <dc:creator>Petr Razumov</dc:creator>
      <pubDate>Thu, 27 May 2021 19:41:17 +0000</pubDate>
      <link>https://dev.to/tidalcloud/how-to-cross-compile-go-app-for-apple-silicon-m1-27l6</link>
      <guid>https://dev.to/tidalcloud/how-to-cross-compile-go-app-for-apple-silicon-m1-27l6</guid>
      <description>&lt;p&gt;This is the first part in the series on how we prepared our application to run on M1 (Apple Silicon).&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://tidalmigrations.com/" rel="noopener noreferrer"&gt;Tidal Migrations&lt;/a&gt; we build our CLI application — &lt;a href="https://get.tidal.sh/" rel="noopener noreferrer"&gt;Tidal Tools&lt;/a&gt; — to make it easier for our customers to deal with all sorts of data necessary for their cloud migrations journey. The CLI app could be run anywhere — on a manager’s Microsoft Windows workstation, on a developer’s Apple MacBook Pro, or even on a Linux server since &lt;em&gt;a long time ago in a datacenter far, far away…&lt;/em&gt; Because of this, having the ability to build the app for different operating systems was the top priority for us since the beginning of the development. That’s why we choose Go programming language for our CLI application development.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://golang.org/" rel="noopener noreferrer"&gt;Go&lt;/a&gt; (sometimes referred as &lt;em&gt;Golang&lt;/em&gt;) is a statically typed, compiled programming language designed at Google. Among other awesome features of Go, there is one which was crucial for us — the ability to cross-compile code for different operating systems and architectures. In other words, it is possible for a developer running, for example, macOS on her laptop to build an application suitable for running on Windows or Linux, or any other operating system which is supported by the Go compiler.&lt;/p&gt;

&lt;p&gt;The following short walkthrough will guide you through the process of creating a simple Hello world application and building it for Linux, Windows and macOS on M1.&lt;/p&gt;

&lt;p&gt;Let’s start with writing the actual code for our application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// hello.go&lt;/span&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"runtime"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"Hello world from %s/%s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GOOS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GOARCH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we build it (with &lt;code&gt;go build hello.go&lt;/code&gt; command) and run the binary (&lt;code&gt;./hello&lt;/code&gt;) it should display something like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello world from linux/amd64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello world from windows/amd64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello world from darwin/arm64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That depends on the OS and architecture of the computer where we build our app. But as I mentioned earlier, it is possible to build Go applications for operating systems and architectures different than the one where we run the build process. With modern Go tools it is pretty straightforward. All we need to do is to set some specific environment variables — &lt;code&gt;GOOS&lt;/code&gt; and &lt;code&gt;GOARCH&lt;/code&gt; — and voilà, we build binaries for different operating systems and architectures.&lt;/p&gt;

&lt;p&gt;At first, let's build our “Hello world” app for Microsoft Windows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GOOS=windows GOARCH=amd64 go build hello.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we run &lt;code&gt;ls&lt;/code&gt;, we'd see that a new file (&lt;code&gt;hello.exe&lt;/code&gt;) appeared in the current directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ls
hello.exe  hello.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's determine a type of the &lt;code&gt;hello.exe&lt;/code&gt; file using &lt;code&gt;file&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ file hello.exe
hello.exe: PE32+ executable (console) x86-64 (stripped to external PDB), for MS Windows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's build our application for some old (32-bit) Linux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GOOS=linux GOARCH=386 go build -o hello-linux-386 hello.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A new file (&lt;code&gt;hello-linux-386&lt;/code&gt;) should appear:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ls
hello.exe  hello.go  hello-linux-386
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it should be of 32-bit executable type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;file hello-linux-386 
hello-linux-386: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, ..., not stripped
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, let's build the same application for Apple Silicon:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GOOS=darwin GOARCH=arm64 go build -o hello-macos-arm64 hello.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running that &lt;code&gt;go build&lt;/code&gt; command should create the third binary in our folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ls
hello.exe  hello.go  hello-linux-386  hello-macos-arm64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The type of the file is the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ file hello-macos-arm64 
hello-macos-arm64: Mach-O 64-bit arm64 executable, flags:&amp;lt;|DYLDLINK|PIE&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsv73xkxrtaik65xgidwc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsv73xkxrtaik65xgidwc.png" alt="Hello world" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it! With this simple trick you can build Go applications for different operating systems and architectures on your laptop or a single build server.&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this post! Stay tuned to learn more about our journey towards M1 version release!&lt;/p&gt;

</description>
      <category>go</category>
      <category>crosscompile</category>
      <category>applesilicon</category>
      <category>m1</category>
    </item>
    <item>
      <title>Use Nmap for faster discovery in Cloud Migrations</title>
      <dc:creator>Mario Mejia</dc:creator>
      <pubDate>Thu, 25 Mar 2021 21:41:37 +0000</pubDate>
      <link>https://dev.to/tidalcloud/use-nmap-for-faster-discovery-in-cloud-migrations-5857</link>
      <guid>https://dev.to/tidalcloud/use-nmap-for-faster-discovery-in-cloud-migrations-5857</guid>
      <description>&lt;p&gt;When starting a cloud migration project, one of the most important and often challenging parts is to have an accurate understanding of what you are trying to migrate. Over time, companies start new projects, which means creating new infrastructure, adding servers, databases, etc. This is a normal part of the development cycle. However, despite best efforts, inventories get out of sync. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“We have 5 inventories and lists of servers, but we don’t trust any of them.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Imagine this simple scenario where a database needs to be migrated from location A to location B. After it is done, the old database in location A must be decommissioned and the infrastructure inventory must be updated. It is not uncommon, that after the migration is finished, the inventory - a CMDB, ops tool or often: an excel file - is adjusted to add the new server and database instance, but no one ever removes the old database from the source inventory. &lt;/p&gt;

&lt;p&gt;Now imagine that type of change occurring many times over the years and it doesn’t take long to realize why people don’t trust their inventory.  Inventory is increasingly becoming ephemeral.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;p&gt;At Tidal Migrations, we empower companies to drive faster cloud migrations with rapid discovery and assessment of their application portfolio. In addition to our own tools, people can use 3rd party tools that they might already have installed via Tidal Migrations API integrations. Today, people can also leverage the widely used &lt;a href="https://nmap.org/" rel="noopener noreferrer"&gt;Nmap&lt;/a&gt; utility to perform exhaustive host discovery and reconcile their infrastructure inventory.&lt;/p&gt;

&lt;p&gt;What is &lt;strong&gt;Nmap&lt;/strong&gt;? &lt;br&gt;
Nmap is a free and open-source network scanner used by security researchers, network administrators, and migration experts alike. It can be used to discover services running on single hosts, as well as vast networks that contain hundreds of thousands of devices across a multitude of subnets.&lt;/p&gt;

&lt;p&gt;Nmap scans a network by sending packets to system network ports, then it analyzes their responses to figure out what ports are open, closed, or filtered by a firewall.  As a result, Nmap can identify devices and services running on a network without you needing to deploy agents or request permissions to servers via SSH or WinRM, etc. &lt;/p&gt;

&lt;p&gt;In addition to infrastructure reconciliation, scanning your network with Nmap can alert you to known security vulnerabilities. For example, it might identify older software that can be exploited by bad actors, as well as providing you with an opportunity to close open ports that shouldn’t be accessible in the first place. &lt;/p&gt;

&lt;p&gt;At Tidal Migrations, we have built capabilities to leverage the output generated by Nmap and send it to our Platform where our users can assess their data, update their inventory, and facilitate their cloud migration journey.&lt;/p&gt;


  &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F47mdr9qacwid2kqzfwzm.png" alt="Host Disovery view"&gt;Nmap integrates directly with Tidal Migrations to help you on getting an accurate inventory for your cloud migration project.
  

&lt;h3&gt;
  
  
  How to Use Nmap
&lt;/h3&gt;

&lt;p&gt;You can find the official binary packages for all major operating systems (Linux, Windows, Mac OS) at &lt;a href="https://nmap.org/" rel="noopener noreferrer"&gt;Nmap’s website&lt;/a&gt;, and while there is a &lt;a href="https://nmap.org/zenmap/" rel="noopener noreferrer"&gt;GUI option&lt;/a&gt; to download, here we will cover how to use the CLI version together with Tidal Migrations.&lt;/p&gt;

&lt;p&gt;Nmap offers a wide range of scanning utilities, such as port scanning, host discovery, service version detection to name a few. The &lt;a href="https://nmap.org/docs.html" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; is very extensive and easy to follow.&lt;/p&gt;

&lt;p&gt;Once Nmap has been installed, you can run &lt;em&gt;nmap -h&lt;/em&gt; at the command prompt at any time. It will display your current version, as well as a breakdown of its commands.&lt;/p&gt;

&lt;p&gt;Its command structure is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nmap &lt;span class="o"&gt;[&lt;/span&gt; &amp;lt;Scan Type&amp;gt; ...] &lt;span class="o"&gt;[&lt;/span&gt; &amp;lt;Options&amp;gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &amp;lt;target specification&amp;gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nmap has sensible defaults, so the most basic command will yield results right away. Simply provide a target, single IP, hostname or a range of IPs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nmap 192.168.1.1-254 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A common scan type is Host Discovery via ICMP Echo (aka “ping”).  To do this, you can use &lt;code&gt;-sn&lt;/code&gt; to disable port scanning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nmap &lt;span class="nt"&gt;-sn&lt;/span&gt; 192.168.1.0/24              &lt;span class="c"&gt;# Ping sweep a subnet&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if you’re curious about how your range expressions are evaluated, you can use &lt;code&gt;-sL&lt;/code&gt; to list targets only, without scanning them at all:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nmap &lt;span class="nt"&gt;-sL&lt;/span&gt; 192.168.1.1-3,10-20         &lt;span class="c"&gt;# No Scan. List targets only &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To say there are a lot of scan types you can run using Nmap is an understatement. In addition to the built-in flags, there is also an entire ecosystem of extensions and plugins that scanning experts employ in those hard to scan environments. To keep this blog from becoming War and Peace, I will share &lt;strong&gt;two&lt;/strong&gt; of my favorite commands to get you started right away.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nmap &lt;span class="nt"&gt;-T2&lt;/span&gt; 192.168.1.0-127
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;-T&lt;/strong&gt;  - Timing mode.  Adjusting the frequency of packets being sent can be used to evade or comply with some of the rules in firewalls or IDS. &lt;br&gt;
When running Nmap in timing mode, you must use either "Paranoid", "Sneaky", "Polite", "Normal", "Aggressive", "Insane" or a number from 0 (Paranoid) being the slowest, waiting 5 minutes between sending each probe to 5 (Insane) which will be easily detectable.&lt;/p&gt;

&lt;p&gt;To more efficiently scan your network, limit yourself to the known ports for common web applications and databases. Also, save your output as XML so you can incorporate the results into your migration planning.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nmap &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-p80&lt;/span&gt;,443,8080,8443,1433,1521,27017 10.0.0.0/24 &lt;span class="nt"&gt;-oX&lt;/span&gt; my_network_scan.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;-sS&lt;/strong&gt; - enables TCP SYN Stealth port scan.&lt;br&gt;
&lt;strong&gt;-p&lt;/strong&gt; - lets you specify the ports you are interested in, therefore improving your scan speed.&lt;br&gt;
&lt;strong&gt;-oX&lt;/strong&gt; - generates an XML file with the output of the scan.&lt;/p&gt;

&lt;p&gt;It is possible to combine a Nmap command and our Tidal Migration integration in just one step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnyd4dncs3vsgpghz7l0i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnyd4dncs3vsgpghz7l0i.png" alt="Nmap - Tidal Migrations"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s break this example into two parts. &lt;br&gt;
To start, Nmap is going to scan an IP Address and its range, the &lt;code&gt;-sV&lt;/code&gt; flag indicates Service and Version detection on open ports, and &lt;code&gt;-oX&lt;/code&gt; declares that we want the output in  XML format.&lt;br&gt;
After the scan is completed, our CLI &lt;a href="https://tidalmigrations.com/tidal-tools/" rel="noopener noreferrer"&gt;Tidal Tools&lt;/a&gt; will pick up the output and send it to your Tidal Migrations account where you can continue your discovery process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Whether you are migrating to the cloud today or just preparing your infrastructure for a future shift. Having a solid understanding of your current inventory and infrastructure is a must.&lt;/p&gt;

&lt;p&gt;After this brief introduction to Nmap, its capabilities, and how to use it, I hope you feel encouraged to use this tool in your cloud migration journey. There is plenty of information on its official website, and a strong community backing it up. &lt;/p&gt;

&lt;p&gt;Happy discovery :)&lt;/p&gt;

&lt;p&gt;-&lt;em&gt;&lt;a href="https://www.linkedin.com/in/mario-mejia" rel="noopener noreferrer"&gt;Mario Mejia&lt;/a&gt;&lt;/em&gt;&lt;br&gt;
Senior Software Engineer, Tidal Migrations&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Want to know more about Tidal Migrations?&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://tidalmigrations.com/tour/" rel="noopener noreferrer"&gt;Tidal Migrations&lt;/a&gt; is a cloud migration tool that gives you this application inventory, discovery, and assessment all in one place. Delivered as a SaaS platform, you can import your spreadsheets or deploy our tools and get moving today. Results in minutes, not months. Migrate with confidence.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nmap</category>
      <category>cloudmigration</category>
      <category>cloud</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
