<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>DevMemo – Blog</title><link>https://devmemo.gitlab.io/blog/</link><description>Recent content in Blog on DevMemo</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Sat, 14 Jan 2023 14:56:50 -0800</lastBuildDate><atom:link href="https://devmemo.gitlab.io/blog/index.xml" rel="self" type="application/rss+xml"/><item><title>Blog: How To Debug Emacs Cpp Indentation</title><link>https://devmemo.gitlab.io/blog/how_to_debug_emacs_cpp_indentation/</link><pubDate>Sat, 26 Aug 2023 21:16:11 +0000</pubDate><guid>https://devmemo.gitlab.io/blog/how_to_debug_emacs_cpp_indentation/</guid><description>
&lt;h2 id="do-syntactic-analysis">Do Syntactic Analysis&lt;/h2>
&lt;p>Do &lt;code>c-show-syntactic-information&lt;/code> or &lt;code>C-c C-s&lt;/code> to get an syntactic analysis first, you should get something like this&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>Syntactic analysis: ((statement-block-intro 930))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>At the same time, emacs will also highlight the anchor point.&lt;/p>
&lt;h2 id="set-c-set-offset">Set &lt;code>c-set-offset&lt;/code>&lt;/h2>
&lt;p>Do the following to adjust the indentation.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>(defun c-indentation-hook ()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (c-set-offset &amp;#39;statement-block-intro 2)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>(add-hook &amp;#39;c-mode-common-hook &amp;#39;c-indentation-hook)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You&amp;rsquo;ll need to reopen the file to allow the above change to take effect.&lt;/p></description></item><item><title>Blog: How To Resize Disk On Linux</title><link>https://devmemo.gitlab.io/blog/how_to_resize_disk_on_linux/</link><pubDate>Thu, 13 Jul 2023 05:40:50 +0000</pubDate><guid>https://devmemo.gitlab.io/blog/how_to_resize_disk_on_linux/</guid><description>
&lt;p>Assuming you have a Linux VM, and you enlarged the disk. Take the following steps on your Linux to apply the changes.&lt;/p>
&lt;h2 id="step-1-resize-the-volume">Step 1: Resize the volume&lt;/h2>
&lt;p>The easiest way to resize your volume is to use cfdisk.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>sudo cfdisk
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is what it looks like on my VM.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span> Disk: /dev/sda
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Size: 64 GiB, 68719476736 bytes, 134217728 sectors
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Label: gpt, identifier: 89F6152F-43FB-4262-8B2E-06EF3344C5A3
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Device Start End Sectors Size Type
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;gt;&amp;gt; /dev/sda1 2048 4095 2048 1M BIOS boot
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> /dev/sda2 4096 134217694 134213599 64G Linux filesystem
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ┌────────────────────────────────────────────────────────────────────────────────────┐
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> │Partition UUID: 1BDD55B2-1581-4671-878E-5891F4802220 │
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> │Partition type: BIOS boot (21686148-6449-6E6F-744E-656564454649) │
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> └────────────────────────────────────────────────────────────────────────────────────┘
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> [ Delete ] [ Resize ] [ Quit ] [ Type ] [ Help ] [ Write ] [ Dump ]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Go to your target device, and then resize it as you need. For my case, it is &lt;code>/dev/sda2&lt;/code>, because that&amp;rsquo;s where the root directory is mounted.&lt;/p>
&lt;h2 id="step-2-apply-the-change-to-your-filesystem">Step 2: Apply the change to your filesystem.&lt;/h2>
&lt;p>To apply the change to your file system, run the following command&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>sudo resize2fs /dev/sda2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now you have added more spaces to your disk.&lt;/p></description></item><item><title>Blog: Set Up VPN Server And VPN Router</title><link>https://devmemo.gitlab.io/blog/set_up_vpn_server_and_vpn_router/</link><pubDate>Sun, 02 Jul 2023 04:57:29 -0700</pubDate><guid>https://devmemo.gitlab.io/blog/set_up_vpn_server_and_vpn_router/</guid><description>
&lt;h2 id="set-up-vpn-server">Set Up VPN Server&lt;/h2>
&lt;p>Deploy an ubuntu 22.04 server on your target network. Run the following commands to install vpn server on it.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>wget https://raw.githubusercontent.com/Angristan/openvpn-install/master/openvpn-install.sh -O ubuntu-22.04-lts-vpn-server.sh
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>chmod -v +x ubuntu-22.04-lts-vpn-server.sh
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo ./ubuntu-22.04-lts-vpn-server.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Once it is done, an ovpn file will be generated, for example,&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>The configuration file has been written to /home/ubuntu/linux.ovpn.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="set-up-vpn-router">Set Up VPN Router&lt;/h2>
&lt;h3 id="step-1-install-an-physical-ubuntu-server">Step 1: Install An Physical Ubuntu Server&lt;/h3>
&lt;p>Install ubuntu on a device which has:&lt;/p>
&lt;ul>
&lt;li>A physical ethernet port connected to your home router. This port will be used to exchange data from and to the Internet.&lt;/li>
&lt;li>A wifi adapter. This port will be used as a hotspot for your devices to connect. Any device connected to this hotspot will be able use send traffic through the vpn server.&lt;/li>
&lt;/ul>
&lt;p>This ubuntu server will be used as your VPN router.&lt;/p>
&lt;h3 id="step-2-set-up-dns">Step 2: Set Up DNS&lt;/h3>
&lt;p>Run the following command to set up your DNS on your VPN router.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>echo &lt;span style="color:#e6db74">&amp;#34;[Resolve]
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">DNS=8.8.8.8 8.8.4.4
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> | sudo tee /etc/systemd/resolved.conf.d/99-dns.conf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Restart your VPN router,&lt;/p>
&lt;h3 id="step-3-create-a-wifi-hotspot">Step 3: Create A Wifi Hotspot&lt;/h3>
&lt;p>Run the following command to create a wifi hotspot.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>sudo nmcli d wifi hotspot ifname &lt;span style="color:#f92672">{&lt;/span>interface-name&lt;span style="color:#f92672">}&lt;/span> ssid &lt;span style="color:#f92672">{&lt;/span>wifi-ssid&lt;span style="color:#f92672">}&lt;/span> password &lt;span style="color:#f92672">{&lt;/span>wifi-passwd&lt;span style="color:#f92672">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="step-4-connect-to-your-vpn-server-from-your-vpn-router">Step 4: Connect To Your VPN Server From Your VPN Router&lt;/h3>
&lt;p>Copy the opvn config file from your VPN server to your VPN router, and run the following command.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>sudo openvpn --config &lt;span style="color:#f92672">{&lt;/span>ovpn-config-file&lt;span style="color:#f92672">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="step-5-enable-nat-on-ubuntu-server">Step 5: Enable NAT On Ubuntu Server&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>iptables -t nat -A POSTROUTING -j MASQUERADE
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This config will go away if you reboot your linux server. To make it persistent, do the following&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>sudo apt update -y &lt;span style="color:#f92672">&amp;amp;&amp;amp;&lt;/span> sudo apt install iptables-persistent
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now your VPN router is ready to use.&lt;/p></description></item><item><title>Blog: How To Use mDNS Within Docker Container</title><link>https://devmemo.gitlab.io/blog/use_mdns_within_docker_container/</link><pubDate>Sat, 17 Jun 2023 09:56:48 -0700</pubDate><guid>https://devmemo.gitlab.io/blog/use_mdns_within_docker_container/</guid><description>
&lt;p>mDNS is a service that allows you to access hosts within your LAN by using their hostnames. However, it is not working by default in a docker container.&lt;/p>
&lt;p>In order to use mDNS in a docker container, you&amp;rsquo;ll have to:&lt;/p>
&lt;ul>
&lt;li>Install &lt;code>avahi-utils&lt;/code> within your container.&lt;/li>
&lt;li>Map these two files into your container, &lt;code>/var/run/dbus&lt;/code> and &lt;code>/var/run/avahi-daemon/socket&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>For example, you can start your container like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>docker run -v /var/run/dbus:/var/run/dbus -v /var/run/avahi-daemon/socket:/var/run/avahi-daemon/socket -it debian:10
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>and then run the following commands within your contain:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>apt update &lt;span style="color:#f92672">&amp;amp;&amp;amp;&lt;/span> apt install avahi-utils -y
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, you can ping your hosts within your LAN, e.g.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>root@cccbde9152f2:/# ping nfs-server.local -c &lt;span style="color:#ae81ff">4&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>PING nfs-server.local &lt;span style="color:#f92672">(&lt;/span>192.168.1.101&lt;span style="color:#f92672">)&lt;/span> 56&lt;span style="color:#f92672">(&lt;/span>84&lt;span style="color:#f92672">)&lt;/span> bytes of data.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">64&lt;/span> bytes from 192.168.1.101 &lt;span style="color:#f92672">(&lt;/span>192.168.1.101&lt;span style="color:#f92672">)&lt;/span>: icmp_seq&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span> ttl&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">63&lt;/span> time&lt;span style="color:#f92672">=&lt;/span>0.193 ms
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">64&lt;/span> bytes from 192.168.1.101 &lt;span style="color:#f92672">(&lt;/span>192.168.1.101&lt;span style="color:#f92672">)&lt;/span>: icmp_seq&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span> ttl&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">63&lt;/span> time&lt;span style="color:#f92672">=&lt;/span>0.208 ms
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">64&lt;/span> bytes from 192.168.1.101 &lt;span style="color:#f92672">(&lt;/span>192.168.1.101&lt;span style="color:#f92672">)&lt;/span>: icmp_seq&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span> ttl&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">63&lt;/span> time&lt;span style="color:#f92672">=&lt;/span>0.241 ms
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">64&lt;/span> bytes from 192.168.1.101 &lt;span style="color:#f92672">(&lt;/span>192.168.1.101&lt;span style="color:#f92672">)&lt;/span>: icmp_seq&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">4&lt;/span> ttl&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">63&lt;/span> time&lt;span style="color:#f92672">=&lt;/span>0.252 ms
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>--- nfs-server.local ping statistics ---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">4&lt;/span> packets transmitted, &lt;span style="color:#ae81ff">4&lt;/span> received, 0% packet loss, time 36ms
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>rtt min/avg/max/mdev &lt;span style="color:#f92672">=&lt;/span> 0.193/0.223/0.252/0.028 ms
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>root@cccbde9152f2:/#
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Blog: Automate Proxmox VM Creation With Cloud-Init</title><link>https://devmemo.gitlab.io/blog/automate_proxmox_vm_creation_with_cloud_init/</link><pubDate>Mon, 06 Feb 2023 15:10:26 -0800</pubDate><guid>https://devmemo.gitlab.io/blog/automate_proxmox_vm_creation_with_cloud_init/</guid><description>
&lt;p>This post gives you step-by-step instructions of how to automate proxmox VM creation. I&amp;rsquo;ll use ubuntu as an example.&lt;/p>
&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>The overall strategy is to use the &lt;a href="https://cloud-images.ubuntu.com/">ubuntu cloud image&lt;/a> to create a Proxmox VM template, and attach a cloud-init disk to it. After that, we can clone the template to create new VM, and new VM will automatically bootstrap itself, e.g., changing the hostname.&lt;/p>
&lt;h2 id="prepare-tools">Prepare tools&lt;/h2>
&lt;p>Install &lt;code>virt-customize&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>apt-get install libguestfs-tools
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="prepare-the-ubuntu-cloud-image">Prepare The Ubuntu Cloud Image&lt;/h2>
&lt;p>Use the following commands to prepare the ubuntu cloud image:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Create a new directory for our image building.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>mkdir ubuntu-cloud-image
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd ubuntu-cloud-image
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Download the cloud image.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Resize the image to 16GB. Feel free to change it to your desired size.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>qemu-img resize noble-server-cloudimg-amd64.img 16G
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Install avahi-deamon for mdns.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>virt-customize -a noble-server-cloudimg-amd64.img --run-command &lt;span style="color:#e6db74">&amp;#39;apt update &amp;amp;&amp;amp; apt install avahi-daemon -y &amp;amp;&amp;amp; cloud-init clean --logs --machine-id --configs all --reboot &amp;amp;&amp;amp; sudo truncate -s 0 /etc/machine-id /var/lib/dbus/machine-id&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="prepare-the-proxmox-vm-template">Prepare The Proxmox VM Template&lt;/h2>
&lt;p>Then, we prepare the Proxmox VM template with the following steps.&lt;/p>
&lt;h3 id="create-a-temporary-vm">Create A Temporary VM&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Create a VM with the following configuration:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># * vmid: 1000 (feel free to change this)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># * memory: 4GB&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># * network: attached to bridge vmbr0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>qm create &lt;span style="color:#ae81ff">1000&lt;/span> --name ubuntu-noble-template --memory &lt;span style="color:#ae81ff">4096&lt;/span> --net0 virtio,bridge&lt;span style="color:#f92672">=&lt;/span>vmbr0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="attach-the-ubuntu-cloud-image">Attach The Ubuntu Cloud Image&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Attach the ubuntu cloud image onto the VM.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Note that here ssd is my storage name. You&amp;#39;ll need to replace it with yours.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>qm importdisk &lt;span style="color:#ae81ff">1000&lt;/span> noble-server-cloudimg-amd64.img ssd
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Make the attached disk scsi0.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>qm set &lt;span style="color:#ae81ff">1000&lt;/span> --scsihw virtio-scsi-pci --scsi0 ssd:vm-1000-disk-0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Set scsi0 as the first boot device.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>qm set &lt;span style="color:#ae81ff">1000&lt;/span> --boot c --bootdisk scsi0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="create-a-cloud-init-drive">Create A Cloud-init Drive&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Create a cloud-init drive.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>qm set &lt;span style="color:#ae81ff">1000&lt;/span> --ide2 ssd:cloudinit
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Configure your cloud-init drive. Replace the username, password and sshkeys for your case.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>qm set &lt;span style="color:#ae81ff">1000&lt;/span> --ciuser swe --cipassword swe-passwd --sshkeys ~/.ssh/id_rsa.pub --ipconfig0 ip&lt;span style="color:#f92672">=&lt;/span>dhcp
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="convert-the-vm-into-a-template">Convert The VM Into A Template&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Convert the VM into a template.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>qm template &lt;span style="color:#ae81ff">1000&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="create-vm">Create VM&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Create a VM using the template, and set the name to ubuntu-vm1.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>qm clone &lt;span style="color:#ae81ff">1000&lt;/span> &lt;span style="color:#ae81ff">1101&lt;/span> --full --name ubuntu-vm1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Create another VM using the template, and set the name to ubuntu-vm2.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>qm clone &lt;span style="color:#ae81ff">1000&lt;/span> &lt;span style="color:#ae81ff">1102&lt;/span> --full --name ubuntu-vm2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To access these VMs, you can do&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Use the following command to access ubuntu-vm1 if you have mdns included in your template, and you are within the same LAN as ubuntu-vm1.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ssh swe@ubuntu-vm1.local
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Use the following command to access ubuntu-vm2 if you have mdns included in your template, and you are within the same LAN as ubuntu-vm2.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ssh swe@ubuntu-vm2.local
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Blog: Automate Ubuntu Installation</title><link>https://devmemo.gitlab.io/blog/automate_ubuntu_installation/</link><pubDate>Wed, 01 Feb 2023 16:22:53 -0800</pubDate><guid>https://devmemo.gitlab.io/blog/automate_ubuntu_installation/</guid><description>
&lt;p>As a software engineer, I frequently needed to create new Linux VMs for testing and other purposes. The traditional way of installing Linux on a VM is very inefficient, which consumes a lot of my time. Automating the process is very appealing to me.&lt;/p>
&lt;p>In this post, I&amp;rsquo;m going to give you step-by-step instructions of how to do that.&lt;/p>
&lt;h2 id="build-ubuntu-auto-install-iso-image">Build Ubuntu Auto-Install Iso Image&lt;/h2>
&lt;h3 id="prerequisites">Prerequisites&lt;/h3>
&lt;p>To build an auto-install ubuntu image, we&amp;rsquo;ll need to install the following packages first&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>sudo apt update &lt;span style="color:#f92672">&amp;amp;&amp;amp;&lt;/span> sudo apt install p7zip wget xorriso whois
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="set-up-build-environment">Set Up Build Environment&lt;/h3>
&lt;p>Next, let&amp;rsquo;s download the latest ubuntu image from the official website. The version we are going to be using is &lt;a href="https://releases.ubuntu.com/22.04.1/ubuntu-22.04.1-live-server-amd64.iso">ubuntu 20.04&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Go to your home directory.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Create a directory for our image building process.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>mkdir ubuntu-auto-install-image-building
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd ubuntu-auto-install-image-building/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Download the latest package.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>wget https://releases.ubuntu.com/22.04.1/ubuntu-22.04.1-live-server-amd64.iso
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Create a source directory.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>mkdir ubuntu-iso-sources
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Unpack the iso to the ubuntu-iso-sources dir.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>7z -y x ubuntu-22.04.1-live-server-amd64.iso -oubuntu-iso-sources
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Go into the source directory.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd ubuntu-iso-sources
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Move BOOT directory outside, since this is not needed in the final iso.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>mv &lt;span style="color:#ae81ff">\[&lt;/span>BOOT&lt;span style="color:#ae81ff">\]&lt;/span> ../BOOT
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="update-image-files">Update Image Files&lt;/h3>
&lt;p>After the source files are unpacked, let&amp;rsquo;s update some files.&lt;/p>
&lt;h4 id="create-an-grub-entry">Create An Grub Entry&lt;/h4>
&lt;p>The first file to update is the grub file, i.e., &lt;code>~/ubuntu-auto-install-image-building/ubuntu-iso-sources/boot/grub/grub.cfg&lt;/code>. We&amp;rsquo;ll need to do two things:&lt;/p>
&lt;ul>
&lt;li>Add an autoinstall entry.&lt;/li>
&lt;li>Make wait time shorter (this is optional).&lt;/li>
&lt;/ul>
&lt;p>The updated file looks like this. The highlighted lines are modified.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;">&lt;code class="language-txt" data-lang="txt">&lt;span style="display:flex; background-color:#3c3d38">&lt;span>set timeout=5
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>loadfont unicode
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>set menu_color_normal=white/black
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>set menu_color_highlight=black/light-gray
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex; background-color:#3c3d38">&lt;span>menuentry &amp;#34;Autoinstall Ubuntu Server&amp;#34; {
&lt;/span>&lt;/span>&lt;span style="display:flex; background-color:#3c3d38">&lt;span> set gfxpayload=keep
&lt;/span>&lt;/span>&lt;span style="display:flex; background-color:#3c3d38">&lt;span> linux /casper/vmlinuz quiet autoinstall ds=nocloud\;s=/cdrom/server/ ---
&lt;/span>&lt;/span>&lt;span style="display:flex; background-color:#3c3d38">&lt;span> initrd /casper/initrd
&lt;/span>&lt;/span>&lt;span style="display:flex; background-color:#3c3d38">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>menuentry &amp;#34;Try or Install Ubuntu Server&amp;#34; {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> set gfxpayload=keep
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> linux /casper/vmlinuz ---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> initrd /casper/initrd
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>grub_platform
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>if [ &amp;#34;$grub_platform&amp;#34; = &amp;#34;efi&amp;#34; ]; then
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>menuentry &amp;#39;Boot from next volume&amp;#39; {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> exit 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>menuentry &amp;#39;UEFI Firmware Settings&amp;#39; {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> fwsetup
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>else
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>menuentry &amp;#39;Test memory&amp;#39; {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> linux16 /boot/memtest86+.bin
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>fi
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="create-file-user-data">Create File user-data&lt;/h4>
&lt;p>Next, we&amp;rsquo;ll need to create a user-data file, &lt;code>~/ubuntu-auto-install-image-building/ubuntu-iso-sources/server/user-data&lt;/code>, with the following content.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#cloud-config&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">autoinstall&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">version&lt;/span>: &lt;span style="color:#ae81ff">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">keyboard&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">layout&lt;/span>: &lt;span style="color:#ae81ff">us&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">identity&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">hostname&lt;/span>: &lt;span style="color:#ae81ff">ubuntu-server&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">password&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;$y$j9T$qiKEYsg5oLCDV.XxExdEB/$41TEJJWV7he/aTyHQEDAWJyfKceDtCYVk.Agh9aAvk3&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">username&lt;/span>: &lt;span style="color:#ae81ff">swe&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">ssh&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">allow-pw&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">install-server&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is a minimum setup, which only creates a user with a password. The password is &lt;code>devmemo-passwd&lt;/code>. The password string above is generated using the following command&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>mkpasswd &lt;span style="color:#e6db74">&amp;#34;devmemo-passwd&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>Note that the above config is really a minimum one. Check out &lt;a href="https://ubuntu.com/server/docs/install/autoinstall-reference">this page&lt;/a> if you need more configurations.&lt;/p>
&lt;/blockquote>
&lt;h4 id="create-file-meta-data">Create File meta-data&lt;/h4>
&lt;p>For our use case, we don&amp;rsquo;t need any data in meta-data file. So we&amp;rsquo;ll just need to create an empty file.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>touch ~/ubuntu-auto-install-image-building/ubuntu-iso-sources/server/meta-data
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="create-an-auto-installation-iso-image">Create An Auto-Installation Iso Image&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Create the auto-installation iso image.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>xorriso -as mkisofs -r &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> -V &lt;span style="color:#e6db74">&amp;#39;Ubuntu 22.04 LTS (Auto Install)&amp;#39;&lt;/span> &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> -o ../ubuntu-22.04-autoinstall.iso &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> --grub2-mbr ../BOOT/1-Boot-NoEmul.img &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> -partition_offset &lt;span style="color:#ae81ff">16&lt;/span> &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> --mbr-force-bootable &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> -append_partition &lt;span style="color:#ae81ff">2&lt;/span> 28732ac11ff8d211ba4b00a0c93ec93b ../BOOT/2-Boot-NoEmul.img &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> -appended_part_as_gpt &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> -iso_mbr_part_type a2a0d0ebe5b9334487c068b6b72699c7 &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> -c &lt;span style="color:#e6db74">&amp;#39;/boot.catalog&amp;#39;&lt;/span> &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> -b &lt;span style="color:#e6db74">&amp;#39;/boot/grub/i386-pc/eltorito.img&amp;#39;&lt;/span> &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> -no-emul-boot -boot-load-size &lt;span style="color:#ae81ff">4&lt;/span> -boot-info-table --grub2-boot-info &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> -eltorito-alt-boot &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> -e &lt;span style="color:#e6db74">&amp;#39;--interval:appended_partition_2:::&amp;#39;&lt;/span> &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> -no-emul-boot .
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Check out the image.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd ..
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ls -alh ubuntu-22.04-autoinstall.iso
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now the ubuntu auto-install image is created. You can use it to auto install ubuntu on physical machines or VMs.&lt;/p>
&lt;h2 id="create-your-vms">Create Your VMs&lt;/h2>
&lt;p>After you finish the last section, you already have an ubuntu auto-installation image. This section is totally optional.&lt;/p>
&lt;p>In this section, I&amp;rsquo;ll demonstrate how to use this image on proxmox.&lt;/p>
&lt;h3 id="upload-the-image-to-proxmox-host">Upload The Image To Proxmox Host&lt;/h3>
&lt;p>First, let&amp;rsquo;s upload the image to the VM host. You can either use the web UI, or directly copy the iso into &lt;code>/var/lib/vz/template/iso&lt;/code>.&lt;/p>
&lt;h3 id="create-a-vm">Create A VM&lt;/h3>
&lt;p>Next, let&amp;rsquo;s create a VM, put the iso into its cdrom, and make the cdrom the boot device.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>qm create &lt;span style="color:#ae81ff">1000&lt;/span> --name vm1000 --cores &lt;span style="color:#ae81ff">4&lt;/span> --memory &lt;span style="color:#ae81ff">8192&lt;/span> &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> --scsi0 file&lt;span style="color:#f92672">=&lt;/span>ssd:32 --net0 virtio,bridge&lt;span style="color:#f92672">=&lt;/span>vmbr0 &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> --cdrom local:iso/ubuntu-22.04-autoinstall.iso
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For example, the above command will create a 4 core, 8GB ram, and 32GB ssd VM. Start the VM, and then all you have to do is to wait until the OS is fully installed.&lt;/p></description></item><item><title>Blog: Debug Gitlab Pages Build Locally</title><link>https://devmemo.gitlab.io/blog/debug_gitlab_pages_build_locally/</link><pubDate>Tue, 24 Jan 2023 16:27:49 -0800</pubDate><guid>https://devmemo.gitlab.io/blog/debug_gitlab_pages_build_locally/</guid><description>
&lt;p>Gitlab pages is an awesome tool, which allows you to publish any static website to the internet.&lt;/p>
&lt;p>One of the biggest frustration I had is that I don&amp;rsquo;t know whether a commit is breaking the build before submitting the changes for CI. I was wishing for a local mode for running the CI job.&lt;/p>
&lt;p>Luckily there is. It takes a few steps, like summarized in the following sections.&lt;/p>
&lt;h2 id="step-1-download-gitlab-runner">Step 1: Download Gitlab Runner&lt;/h2>
&lt;p>Before anything, let&amp;rsquo;s set your arch environment variable. If you are using &lt;code>amd64&lt;/code>, do it like this&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>export arch&lt;span style="color:#f92672">=&lt;/span>amd64
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then, go to a download folder, and then download the gitlab runner image&lt;/p>
&lt;p>For Ubuntu or Debian&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Replace ${arch} with any of the supported architectures, e.g. amd64, arm, arm64&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># A full list of architectures can be found here https://gitlab-runner-downloads.s3.amazonaws.com/latest/index.html&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>curl -LJO &lt;span style="color:#e6db74">&amp;#34;https://gitlab-runner-downloads.s3.amazonaws.com/latest/deb/gitlab-runner_&lt;/span>&lt;span style="color:#e6db74">${&lt;/span>arch&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">.deb&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For CentOS or Red Hat Enterprise Linux&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Replace ${arch} with any of the supported architectures, e.g. amd64, arm, arm64&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># A full list of architectures can be found here https://gitlab-runner-downloads.s3.amazonaws.com/latest/index.html&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>curl -LJO &lt;span style="color:#e6db74">&amp;#34;https://gitlab-runner-downloads.s3.amazonaws.com/latest/rpm/gitlab-runner_&lt;/span>&lt;span style="color:#e6db74">${&lt;/span>arch&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">.rpm&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="step-2-install-gitlab-runner">Step 2: Install Gitlab Runner&lt;/h2>
&lt;p>To install gitlab runner on Ubuntu or Debian,&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>sudo dpkg -i &lt;span style="color:#e6db74">&amp;#34;gitlab-runner_&lt;/span>$arch&lt;span style="color:#e6db74">.deb&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To install gitlab runner on CentOS or Red Hat Enterprise Linux&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>sudo rpm -i &lt;span style="color:#e6db74">&amp;#34;gitlab-runner_&lt;/span>$arch&lt;span style="color:#e6db74">.rpm&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="step-3-register-your-runner">Step 3: Register Your Runner&lt;/h2>
&lt;p>Next, let&amp;rsquo;s register your runner. To register your runner, you&amp;rsquo;ll first need to acquire a token from gitlab. Go to your project, and then &lt;strong>Settings -&amp;gt; CD/CD -&amp;gt; Runner -&amp;gt; Project Runners&lt;/strong>. You&amp;rsquo;ll see something like this&lt;/p>
&lt;p align="center">
&lt;img src="https://devmemo.gitlab.io/blog/images/debug_gitlab_pages_build_locally/gitlab_project_runner.png" style="width:60%" alt="gitlab project runner"/>
&lt;/p>
&lt;p>Grab the registration token, and run the following command&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>sudo gitlab-runner register --url https://gitlab.com/ --registration-token $REGISTRATION_TOKEN
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>The executor I selected is &lt;code>docker+machine&lt;/code>, because I found it is used in the gitlab CI.&lt;/li>
&lt;li>The docker image I used is the hugo image &lt;code>registry.gitlab.com/pages/hugo/hugo_extended:latest&lt;/code>.&lt;/li>
&lt;/ul>
&lt;h2 id="step-4-run-build-job-locally">Step 4: Run Build Job Locally&lt;/h2>
&lt;p>Once you have finish the previous step, use the following command to run your job locally&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>gitlab-runner exec docker &amp;lt;job-name&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>job-name&lt;/code> is defined in your &lt;code>.gitlab-ci.yml&lt;/code> file. For me, the real command to run is:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>gitlab-runner exec docker pages
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Blog: Using LVM For Proxmox</title><link>https://devmemo.gitlab.io/blog/using_lvm_for_proxmox/</link><pubDate>Thu, 19 Jan 2023 15:02:22 -0800</pubDate><guid>https://devmemo.gitlab.io/blog/using_lvm_for_proxmox/</guid><description>
&lt;p>If you are trying to use Proxmox to set up a homelab for the first time, it must be very confusing to see all the storage related terms, e.g., LVM, LVM-Thin. This post talks about everything you need to know to understand how Proxmox uses LVM as its storage infrastructure.&lt;/p>
&lt;h2 id="lvm-basics">LVM Basics&lt;/h2>
&lt;p>LVM (Logical Volume Management) is actually a Linux technology. It is not Proxmox proprietary. Proxmox can leverage LVM as its storage infrastructure, but LVM itself is not part of Proxmox.&lt;/p>
&lt;p>Let&amp;rsquo;s try to understand the basics of LVM, and then we try to understand how Proxmox uses it.&lt;/p>
&lt;h3 id="what-is-lvm">What Is LVM?&lt;/h3>
&lt;p>The overall goal of LVM is to create a virtual storage layer on top of the physical storage layer, so that the storage usage can be more flexible.&lt;/p>
&lt;p>For example, if you have two 1TB disks, but you want to use them together as your root directory. LVM provides a solution to this.&lt;/p>
&lt;h3 id="how-does-lvm-work">How Does LVM Work?&lt;/h3>
&lt;p>The following image presents the overall architecture of LVM. (The image comes from &lt;a href="https://wiki.itcollege.ee/index.php?title=File:Lvm%271.jpg">wikipedia&lt;/a>)&lt;/p>
&lt;p align="center">
&lt;img src="https://devmemo.gitlab.io/blog/images/using_lvm_for_proxmox/lvm.jpg" style="width:70%" alt="lvm"/>
&lt;/p>
&lt;p>There are a couple of layers involved (from bottom to top):&lt;/p>
&lt;ul>
&lt;li>On the bottom layer, we have the physical disk itself. These are your hard drives.&lt;/li>
&lt;li>Above the hard drives, we have partitions. Each hard drive is splitted into a few partitions.&lt;/li>
&lt;li>PV (physical volume) is a LVM concept. It is a representation of a physical volume. It is mapped to a single disk partition.&lt;/li>
&lt;li>VG (volume group) is also a LVM concept. A VG contains a couple of PVs.&lt;/li>
&lt;li>LV (logical volume) is a logical volume, which is basically a virtual volume.&lt;/li>
&lt;li>On top of each LV, a file system can be created, and the LV device can be mounted to an accessible directory.&lt;/li>
&lt;/ul>
&lt;p>In this design, logical volume does not have to tie to a single physical disk. Logical volume can be created over multiple physical disks.&lt;/p>
&lt;h3 id="installation-lvm">Installation LVM&lt;/h3>
&lt;p>On debian and ubuntu, you can do&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>sudo apt install lvm2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="how-to-use-lvm">How To Use LVM?&lt;/h3>
&lt;p>Here are some commands to manage PV, VG and LV.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Create a PV&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>pvcreate &amp;lt;disk-device-name&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Remove a PV&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>pvremove &amp;lt;disk-device-name&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># List all PVs&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>pvs
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Create a VG&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>vgcreate &amp;lt;vg-name&amp;gt; &amp;lt;disk-device-name&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Remove a VG&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>vgremove &amp;lt;vg-name&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># List all VGs&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>vgs
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Create a LV&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>lvcreate -L &amp;lt;lv-size&amp;gt; -n &amp;lt;lv-name&amp;gt; &amp;lt;vg-name&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Remove a LV&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>lvremove &amp;lt;vg-name&amp;gt;/&amp;lt;lv-name&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># List all LVs&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>lvs
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="an-example">An Example&lt;/h4>
&lt;p>For example, I have a empty &lt;code>/dev/sdb&lt;/code> device,&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>swe@ubuntu-server:~$ lsblk
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>loop0 7:0 &lt;span style="color:#ae81ff">0&lt;/span> 63.3M &lt;span style="color:#ae81ff">1&lt;/span> loop /snap/core20/1778
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>loop1 7:1 &lt;span style="color:#ae81ff">0&lt;/span> 141.4M &lt;span style="color:#ae81ff">1&lt;/span> loop /snap/docker/2285
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>loop2 7:2 &lt;span style="color:#ae81ff">0&lt;/span> 55.6M &lt;span style="color:#ae81ff">1&lt;/span> loop /snap/core18/2667
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>loop3 7:3 &lt;span style="color:#ae81ff">0&lt;/span> 55.4M &lt;span style="color:#ae81ff">1&lt;/span> loop /snap/core18/2066
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>loop5 7:5 &lt;span style="color:#ae81ff">0&lt;/span> 67.6M &lt;span style="color:#ae81ff">1&lt;/span> loop /snap/lxd/20326
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>loop6 7:6 &lt;span style="color:#ae81ff">0&lt;/span> 49.6M &lt;span style="color:#ae81ff">1&lt;/span> loop /snap/snapd/17883
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>loop7 7:7 &lt;span style="color:#ae81ff">0&lt;/span> 49.8M &lt;span style="color:#ae81ff">1&lt;/span> loop /snap/snapd/17950
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>loop8 7:8 &lt;span style="color:#ae81ff">0&lt;/span> 91.9M &lt;span style="color:#ae81ff">1&lt;/span> loop /snap/lxd/24061
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>loop9 7:9 &lt;span style="color:#ae81ff">0&lt;/span> 139.4M &lt;span style="color:#ae81ff">1&lt;/span> loop /snap/docker/2343
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sda 8:0 &lt;span style="color:#ae81ff">0&lt;/span> 32G &lt;span style="color:#ae81ff">0&lt;/span> disk
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>├─sda1 8:1 &lt;span style="color:#ae81ff">0&lt;/span> 1M &lt;span style="color:#ae81ff">0&lt;/span> part
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>└─sda2 8:2 &lt;span style="color:#ae81ff">0&lt;/span> 32G &lt;span style="color:#ae81ff">0&lt;/span> part /
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sdb 8:16 &lt;span style="color:#ae81ff">0&lt;/span> 32G &lt;span style="color:#ae81ff">0&lt;/span> disk
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sr0 11:0 &lt;span style="color:#ae81ff">1&lt;/span> 4M &lt;span style="color:#ae81ff">0&lt;/span> rom
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sr1 11:1 &lt;span style="color:#ae81ff">1&lt;/span> 1024M &lt;span style="color:#ae81ff">0&lt;/span> rom
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>swe@ubuntu-server:~$
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>let&amp;rsquo;s create a PV on it&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>swe@ubuntu-server:~$ sudo pvcreate /dev/sdb
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Physical volume &lt;span style="color:#e6db74">&amp;#34;/dev/sdb&amp;#34;&lt;/span> successfully created.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>swe@ubuntu-server:~$ sudo pvs
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> PV VG Fmt Attr PSize PFree
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> /dev/sdb lvm2 --- 32.00g 32.00g
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>swe@ubuntu-server:~$
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>and then create a VG on the PV&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>swe@ubuntu-server:~$ sudo vgcreate test-vg /dev/sdb
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Volume group &lt;span style="color:#e6db74">&amp;#34;test-vg&amp;#34;&lt;/span> successfully created
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>swe@ubuntu-server:~$ sudo vgs
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> VG &lt;span style="color:#75715e">#PV #LV #SN Attr VSize VFree&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> test-vg &lt;span style="color:#ae81ff">1&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span> wz--n- &amp;lt;32.00g &amp;lt;32.00g
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>swe@ubuntu-server:~$
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>and then create a LV on the VG&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>swe@ubuntu-server:~$ sudo lvcreate -L 10G -n test-lv test-vg
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Logical volume &lt;span style="color:#e6db74">&amp;#34;test-lv&amp;#34;&lt;/span> created.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>swe@ubuntu-server:~$ sudo lvs
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> test-lv test-vg -wi-a----- 10.00g
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>swe@ubuntu-server:~$
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now we can create a file system on the LG, and mount it for real usage:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>swe@ubuntu-server:~$ sudo mkfs.ext4 /dev/test-vg/test-lv
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>mke2fs 1.45.5 &lt;span style="color:#f92672">(&lt;/span>07-Jan-2020&lt;span style="color:#f92672">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Discarding device blocks: &lt;span style="color:#66d9ef">done&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Creating filesystem with &lt;span style="color:#ae81ff">2621440&lt;/span> 4k blocks and &lt;span style="color:#ae81ff">655360&lt;/span> inodes
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Filesystem UUID: 5c329fcf-76ec-450a-8d96-dfb816538e3e
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Superblock backups stored on blocks:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 32768, 98304, 163840, 229376, 294912, 819200, 884736, &lt;span style="color:#ae81ff">1605632&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Allocating group tables: &lt;span style="color:#66d9ef">done&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Writing inode tables: &lt;span style="color:#66d9ef">done&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Creating journal &lt;span style="color:#f92672">(&lt;/span>&lt;span style="color:#ae81ff">16384&lt;/span> blocks&lt;span style="color:#f92672">)&lt;/span>: &lt;span style="color:#66d9ef">done&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Writing superblocks and filesystem accounting information: &lt;span style="color:#66d9ef">done&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>swe@ubuntu-server:~$ cd /
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>swe@ubuntu-server:/$ sudo mkdir mount
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>swe@ubuntu-server:/$ sudo mount /dev/test-vg/test-lv /mount
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>swe@ubuntu-server:/$ cd mount/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>swe@ubuntu-server:/mount$ ls
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>lost+found
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>swe@ubuntu-server:/mount$
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, we have an ext4 filesystem on the LV:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>swe@ubuntu-server:/mount$ lsblk -f
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>NAME FSTYPE LABEL UUID FSAVAIL FSUSE% MOUNTPOINT
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>loop0 squashfs &lt;span style="color:#ae81ff">0&lt;/span> 100% /snap/core20/1778
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>loop1 squashfs &lt;span style="color:#ae81ff">0&lt;/span> 100% /snap/docker/2285
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>loop2 squashfs &lt;span style="color:#ae81ff">0&lt;/span> 100% /snap/core18/2667
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>loop3 squashfs &lt;span style="color:#ae81ff">0&lt;/span> 100% /snap/core18/2066
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>loop5 squashfs &lt;span style="color:#ae81ff">0&lt;/span> 100% /snap/lxd/20326
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>loop6 squashfs &lt;span style="color:#ae81ff">0&lt;/span> 100% /snap/snapd/17883
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>loop7 squashfs &lt;span style="color:#ae81ff">0&lt;/span> 100% /snap/snapd/17950
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>loop8 squashfs &lt;span style="color:#ae81ff">0&lt;/span> 100% /snap/lxd/24061
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>loop9 squashfs &lt;span style="color:#ae81ff">0&lt;/span> 100% /snap/docker/2343
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sda
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>├─sda1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>└─sda2 ext4 761b4178-9020-4642-ac79-d172a2a0a06d 21G 28% /
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sdb LVM2_member biMIK2-WI2D-ypLl-tVGw-p8hZ-CynW-vo8swy
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>└─test--vg-test--lv ext4 5c329fcf-76ec-450a-8d96-dfb816538e3e 9.2G 0% /mount
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sr0 iso9660 cidata 2023-01-12-13-13-29-00
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sr1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="how-does-proxmox-use-lvm">How Does Proxmox Use LVM?&lt;/h2>
&lt;p>Now you have a basic understanding of LVM. So how does Proxmox use LVM as its storage infrastructure?&lt;/p>
&lt;p>In a few words, Proxmox uses LVs for two purposes:&lt;/p>
&lt;ul>
&lt;li>Use LVs as the client OS disks.&lt;/li>
&lt;li>Use a LV as the host OS root directory.&lt;/li>
&lt;/ul>
&lt;p>For example,&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>root@host3:~$ pvs
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> PV VG Fmt Attr PSize PFree
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> /dev/nvme0n1p3 ssd lvm2 a-- &amp;lt;1.82t 738.50g
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>root@host3:~$
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>root@host3:~$ vgs
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> VG &lt;span style="color:#75715e">#PV #LV #SN Attr VSize VFree&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ssd &lt;span style="color:#ae81ff">1&lt;/span> &lt;span style="color:#ae81ff">13&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span> wz--n- &amp;lt;1.82t 738.50g
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>root@host3:~$
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>root@host3:~$ lvs
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> root ssd -wi-ao---- 96.00g
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> vm-1001-disk-0 ssd -wi-ao---- 16.00g
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> vm-2000-cloudinit ssd -wi-ao---- 4.00m
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> vm-2000-disk-0 ssd -wi-ao---- 16.00g
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> vm-2002-cloudinit ssd -wi-ao---- 4.00m
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> vm-2002-disk-0 ssd -wi-ao---- 16.00g
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> vm-5000-disk-0 ssd -wi-ao---- 200.00g
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> vm-7000-disk-0 ssd -wi-a----- 16.00g
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> vm-7000-disk-1 ssd -wi-a----- 500.00g
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> vm-8000-cloudinit ssd -wi-a----- 4.00m
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> vm-8000-disk-0 ssd -wi-a----- 32.00g
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> vm-8000-disk-1 ssd -wi-a----- 200.00g
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> vm-8001-disk-0 ssd -wi-a----- 32.00g
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>root@host3:~$
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The above commands shows that on one of my Proxmox servers, I have a PV, &lt;code>/dev/nvme0n1p3&lt;/code>, a VG, &lt;code>ssd&lt;/code>, and a few LVs. Each of the client OS has its own LVs.&lt;/p>
&lt;p>Using LV as the host OS storage device makes a lot of sense, which is the whole point of LVM.&lt;/p>
&lt;h2 id="lvm-thin">LVM-Thin&lt;/h2>
&lt;p>By default, Proxmox does not use LVM. Instead, it uses LVM-thin. The difference between LVM-thin and LVM is that:&lt;/p>
&lt;ul>
&lt;li>LVM allocates your storage blocks when you create LVs. With LVM, your LV&amp;rsquo;s storage is guaranteed for your use.&lt;/li>
&lt;li>LVM-thin allocates your storage blocks when you write data to LVs. LVM-thin provides a higher disk utilization, but it can&amp;rsquo;t guarantee you can use all your LV storage.&lt;/li>
&lt;/ul></description></item><item><title>Blog: Create Multi-Boot Usb Flash Drive With Ventoy</title><link>https://devmemo.gitlab.io/blog/create_multi_boot_usb_flash_drive_with_ventoy/</link><pubDate>Wed, 18 Jan 2023 13:38:32 -0800</pubDate><guid>https://devmemo.gitlab.io/blog/create_multi_boot_usb_flash_drive_with_ventoy/</guid><description>
&lt;p>A multi-boot usb drive allows you to install multiple operating systems from the it. It is pretty handy. With a multi-boot usb drive, you don&amp;rsquo;t need to create a installation usb drive for each OS.&lt;/p>
&lt;p>This post gives you step by step instructions to create a multi-boot usb drive.&lt;/p>
&lt;h2 id="step-1-install-ventoy">Step 1: Install Ventoy&lt;/h2>
&lt;p>First of all, download the ventoy installer, from this &lt;a href="https://www.ventoy.net/en/download.html">page&lt;/a>.&lt;/p>
&lt;p>Find a usb drive. Better to have a decent size, e.g., 64G or 128G, so that you can place multiple OS images in it.&lt;/p>
&lt;p>Open the ventoy installer,&lt;/p>
&lt;p align="center">
&lt;img src="https://devmemo.gitlab.io/blog/images/create_multi_boot_usb_flash_drive_with_ventoy/ventoy.jpg" style="width:40%" alt="ventoy"/>
&lt;/p>
&lt;p>and select the target usb drive, and make the ventoy usb flash drive.&lt;/p>
&lt;blockquote>
&lt;p>NOTE: The above snapshot was taken from a Windows machine. On Linux, ventoy has a similar installer.&lt;/p>
&lt;/blockquote>
&lt;h2 id="step-2-copy-the-os-images">Step 2: Copy The OS Images&lt;/h2>
&lt;p>The next thing is to download your target OS installation images, and copy them into the flash drive.&lt;/p>
&lt;p align="center">
&lt;img src="https://devmemo.gitlab.io/blog/images/create_multi_boot_usb_flash_drive_with_ventoy/ventoy_usb_flash_drive.jpg" style="width:80%" alt="ventoy usb flash drive"/>
&lt;/p>
&lt;p>In the above example, I copied five images.&lt;/p>
&lt;h2 id="step-3-boot-your-pc-with-the-multi-boot-usb-drive">Step 3: Boot Your Pc With The Multi-Boot Usb Drive&lt;/h2>
&lt;p>After your multi-boot usb drive is prepared, insert it into the target machine, and boot it up.&lt;/p>
&lt;p align="center">
&lt;img src="https://devmemo.gitlab.io/blog/images/create_multi_boot_usb_flash_drive_with_ventoy/ventoy_installation.jpg" style="width:80%" alt="ventoy installation"/>
&lt;/p>
&lt;p>You&amp;rsquo;ll see the above menu, which allows you to select the target OS to install.&lt;/p></description></item><item><title>Blog: Build Static Website With Hugo And Gitlab</title><link>https://devmemo.gitlab.io/blog/build_static_website_with_hugo_and_gitlab/</link><pubDate>Mon, 16 Jan 2023 16:21:53 -0800</pubDate><guid>https://devmemo.gitlab.io/blog/build_static_website_with_hugo_and_gitlab/</guid><description>
&lt;p>This article talks about everything you need to know to build an internet accessible static website, like the one you are seeing right now.&lt;/p>
&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>This section introduces the key concepts of a static websites.&lt;/p>
&lt;h3 id="what-is-static-website">What Is Static Website?&lt;/h3>
&lt;p>&lt;strong>A static website is basically a website which only serves static files to your browser&lt;/strong>. These files are served as they are stored on the server side. Once your browser gets back the files, it will not circle back to the server to query more information. Any action needed to render your page needs to happen on your browser side. A great example is &lt;a href="https://en.wikipedia.org/wiki/Main_Page">wikipedia&lt;/a>.&lt;/p>
&lt;p>A dynamic website behaves differently. The server side not only serves static files to the browser, but also respond to API calls from the broswer. For example, amazon would need to call server side APIs to place your orders.&lt;/p>
&lt;p>Dynamic websites are definitely more powerful, because it allows you to interact with the server. Static websites are less powerful, but it&amp;rsquo;s a lot easier to maintain. You can even get free internet static website servers today.&lt;/p>
&lt;h3 id="why-hugo">Why Hugo?&lt;/h3>
&lt;p>There are many ways to build static websites. There seems to be two straightforward solutions.&lt;/p>
&lt;p>&lt;strong>Option 1: Writing your website manually&lt;/strong>&lt;/p>
&lt;p>The most straightforward one is to write html, css, js files manually. Although it is perfectly doable, it is not quite desirable, because there are many duplicated work which can be automated. Here&amp;rsquo;s what we need to do for a static website typically (for example, this website):&lt;/p>
&lt;ul>
&lt;li>Build a navigation menu on the top, and include articles in the websites.&lt;/li>
&lt;li>Build a left side structure tree.&lt;/li>
&lt;li>Build a table of content for each article on the right.&lt;/li>
&lt;li>Build a tagging system for the website, and users can search articles based on tags.&lt;/li>
&lt;/ul>
&lt;p>Ideally, website developers should only need to focus on providing the content. If we were to do it without a static website generator, it doesn&amp;rsquo;t seem to be that easy. Everytime you add a new article, you&amp;rsquo;ll have to carefully update the above items, which is definitely a nightmare. That being said, building a static website manually is not preferred, because there are two many duplicated manual work.&lt;/p>
&lt;p>&lt;strong>Option 2: Using pure python to generate your website&lt;/strong>&lt;/p>
&lt;p>So certain kind of automation needs to be done here. Can we use pure python to do that? Of course we can. However, pure python doesn&amp;rsquo;t provide a html template system. Writing python code to generate html files look ugly. Part of the python code needs to generate the texts, and part of it needs to generate automated items. As a developer, you are not focusing on the content itself, but the python script to generate the content. On the other hand, python definitely does not offer you the best performance, because it is a scripting language. So using pure python to automate the website generation is also not the best way.&lt;/p>
&lt;p>Both options are not the most desirable one, but it makes us realize that we need:&lt;/p>
&lt;ul>
&lt;li>A html template system, which allows us to define html files with auto-generated component in it.&lt;/li>
&lt;li>A tool to generate the final output fast enough.&lt;/li>
&lt;/ul>
&lt;p>Hugo works exactly like this. Hugo built a html template system on top of go. Go is definitely fast enough, and the template system allows developers to truly focus on writing the content itself.&lt;/p>
&lt;h3 id="why-gitlab">Why Gitlab?&lt;/h3>
&lt;p>Gitlab is one of the best places in the internet to host free static websites. You can also manage your repo there, and gitlab provides CI/CD for you to auto build and deploy your change.&lt;/p>
&lt;p>Another choice here is github, but I found there are two major differences between them.&lt;/p>
&lt;ul>
&lt;li>In github, each user can only host one single static websites. While in gitlab, you can create many groups, and each group can have one website hosted.&lt;/li>
&lt;li>In github, you can only host the website if you open source your project (unless you pay for it). In github, your source code doesn&amp;rsquo;t have to be open-sourced.&lt;/li>
&lt;/ul>
&lt;p>So Gitlab is clearly a better choice here in terms of the above factors.&lt;/p>
&lt;h2 id="development">Development&lt;/h2>
&lt;p>This section talks about how to develop a website with hugo, and deploy it on gitlab pages.&lt;/p>
&lt;h3 id="step-1-create-a-gitlab-group-and-a-project">Step 1: Create A Gitlab Group And A Project&lt;/h3>
&lt;p>Log on gitlab, and go to &lt;a href="https://gitlab.com/groups/new">https://gitlab.com/groups/new&lt;/a>. Fill up the forms&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Group name&lt;/strong>: let&amp;rsquo;s use examplesite.&lt;/li>
&lt;li>&lt;strong>Group URL&lt;/strong>: it&amp;rsquo;s filled up automatically based on your group name.&lt;/li>
&lt;li>&lt;strong>Visibility level&lt;/strong>: this is to say whether you repo is publicly visible. Either private or public is ok.&lt;/li>
&lt;li>&lt;strong>Role&lt;/strong>: Software Developer (or others based on your case).&lt;/li>
&lt;li>&lt;strong>Who will be using this group&lt;/strong>: Just me (or My company of team if you have a team).&lt;/li>
&lt;li>&lt;strong>What will you use this group for?&lt;/strong>: I want to store my code.&lt;/li>
&lt;/ul>
&lt;p>Then click on create.&lt;/p>
&lt;p>Once a group is created, create an blank project in it:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Project name&lt;/strong>: This has to be &lt;strong>examplesite.gitlab.io&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Project deployment target&lt;/strong>: Gitlab Pages&lt;/li>
&lt;/ul>
&lt;p>Then create project.&lt;/p>
&lt;p>Now let&amp;rsquo;s go back to our mac or Linux server, and find a place (say home folder) to clone your project from gitlab:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>git clone https://gitlab.com/examplesite/examplesite.gitlab.io.git
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>you can also clone by ssh if you want,&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>git clone git@gitlab.com:examplesite/examplesite.gitlab.io.git
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now you have an empty git project on gitlab, which is cloned into your &lt;code>~/examplesite&lt;/code> directory.&lt;/p>
&lt;h3 id="step-2-create-an-empty-hugo-website">Step 2: Create An Empty Hugo Website&lt;/h3>
&lt;p>To use hugo, we&amp;rsquo;ll first need to install it. To install hugo on ubuntu,&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>sudo apt install hugo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To install hugo on mac,&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>brew install hugo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>After hugo is installed, let&amp;rsquo;s create a hugo website by typing in the following command from your home folder.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>cd
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>hugo new site examplesite.gitlab.io --force
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note we need the parameter &lt;code>--force&lt;/code> because the examplesite directory already exists.&lt;/p>
&lt;p>Next, we need to copy a theme. There are many themes available in &lt;a href="https://themes.gohugo.io/">here&lt;/a>. In this example, we&amp;rsquo;ll use the &lt;a href="https://github.com/theNewDynamic/gohugo-theme-ananke">ananke&lt;/a> theme.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>cd examplesite.gitlab.io
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git submodule add https://github.com/theNewDynamic/gohugo-theme-ananke themes/ananke
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>echo &lt;span style="color:#e6db74">&amp;#34;theme = &amp;#39;ananke&amp;#39;&amp;#34;&lt;/span> &amp;gt;&amp;gt; config.toml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>After that, we can start our hugo website by typing in&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>hugo server --bind 0.0.0.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We bind the ip 0.0.0.0 so that you can access it from outside the server. Then you can access your server from {your-server-ip}:1313. If your hugo server is running on the same machine with your browser, you can use localhost:1313. In my case, I got&lt;/p>
&lt;p align="center">
&lt;img src="https://devmemo.gitlab.io/blog/images/built_static_website_with_hugo_and_gitlab/example_hugo_website.jpg" style="width:80%" alt="example hugo website"/>
&lt;/p>
&lt;h3 id="step-3-create-a-post">Step 3: Create A Post&lt;/h3>
&lt;p>Next, let&amp;rsquo;s create a post on the website. To create a post, type in:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>hugo new posts/my-first-post.md
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Edit the file &lt;code>content/posts/my-first-post.md&lt;/code> and update its content like the following:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-markdown" data-lang="markdown">&lt;span style="display:flex;">&lt;span>---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>title: &amp;#34;My First Post&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>date: 2023-01-17T07:19:39Z
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>tags: [&amp;#34;post&amp;#34;]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>This is my first hugo post!
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then go to the page again, and you&amp;rsquo;ll find the post.&lt;/p>
&lt;h3 id="step-4-submit-our-git-changes">Step 4: Submit Our Git Changes&lt;/h3>
&lt;p>Now, submit our changes onto gitlab.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>git add -A
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git commit -m &lt;span style="color:#e6db74">&amp;#34;Example hugo website&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git push origin main:main
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="step-5-host-it-on-gitlab-pages">Step 5: Host It On Gitlab Pages&lt;/h3>
&lt;p>Now, it is time to deploy it to gitlab pages and make it public to internet.&lt;/p>
&lt;h4 id="step-51-create-a-gitlab-ciyml">Step 5.1: Create A .gitlab-ci.yml&lt;/h4>
&lt;p>First, let&amp;rsquo;s create a &lt;code>.gitlab-ci.yml&lt;/code> file in your repo root directory to trigger the gitlab CI/CD with the following content.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">image&lt;/span>: &lt;span style="color:#ae81ff">registry.gitlab.com/pages/hugo/hugo_extended:latest&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">before_script&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">apk add --update nodejs npm&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">npm install --prefix themes/ananke&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">variables&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">GIT_SUBMODULE_STRATEGY&lt;/span>: &lt;span style="color:#ae81ff">recursive&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">pages&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">script&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">hugo -e production --minify&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">artifacts&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">paths&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">public&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">rules&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#f92672">if&lt;/span>: &lt;span style="color:#ae81ff">$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then, submit the file with the following command.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>git add -A
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git commit -m &lt;span style="color:#e6db74">&amp;#34;Add .gitlab-ci.yml file&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git push origin main:main
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, if you go to your gitlab project CI/CD -&amp;gt; Pipelines, you should see your commit is triggering a new build and deployment. After your pipeline job is done, you should be able to see your website from &lt;a href="https://examplesite.gitlab.io/">https://examplesite.gitlab.io/&lt;/a>.&lt;/p>
&lt;h4 id="step-52-make-your-pages-public-to-internet">Step 5.2: Make Your Pages Public To Internet&lt;/h4>
&lt;p>Although you are able to see the website from a public url, others cannot. You&amp;rsquo;ll have to make it public. To do that, Go to your project -&amp;gt; Settings -&amp;gt; Visibility, project features, permissions -&amp;gt; Pages, select Everyone. Then goto the bottom and &lt;strong>save changes&lt;/strong>.&lt;/p>
&lt;p>Now a hugo static website on the Internet is done! For any of the future changes, just edit your hugo website locally, and then push the changes to the gitlab main branch. Then your website will be updated automatically.&lt;/p></description></item><item><title>Blog: Efficient Mac Window Management</title><link>https://devmemo.gitlab.io/blog/mac_window_management/</link><pubDate>Sat, 14 Jan 2023 14:55:09 -0800</pubDate><guid>https://devmemo.gitlab.io/blog/mac_window_management/</guid><description>
&lt;p>As a software engineer, you might need to open up multiple windows at the same time when doing development work, e.g., a terminal window, a browser, a IDE window, a chat application, etc. You might also have multiple monitors as well. Window management becomes a neccessary part of your work. Unfortunately, mac doesn&amp;rsquo;t come with a decent solution to this.&lt;/p>
&lt;p>In this article, I&amp;rsquo;ll talk about how you can effectively manage your windows on your mac. I&amp;rsquo;ll cover two topics specifically:&lt;/p>
&lt;ul>
&lt;li>How to switch between windows?&lt;/li>
&lt;li>How to place the target window on the desired position?&lt;/li>
&lt;/ul>
&lt;h2 id="window-switching">Window Switching&lt;/h2>
&lt;p>Switching between windows is probably one of the most common operations you&amp;rsquo;ll need when using any kind of operating system.&lt;/p>
&lt;p>If you&amp;rsquo;ve used a Windows Operating System before, you&amp;rsquo;ll know that it provides a pretty good solution to switch among windows, which is &lt;code>ctrl + tab&lt;/code>. Whenever you press &lt;code>ctrl + tab&lt;/code>, you&amp;rsquo;ll see a window poping up in the middle of your screen, presenting you all the current visible windows. By selecting the target window, you can switch to it pretty easily.&lt;/p>
&lt;p>This feature works really well on Windows, and Linux also provides the same feature. However, for some reason, mac doesn&amp;rsquo;t do that by default. Mac has a &lt;code>cmd + tab&lt;/code> hotkey, and it pops up a similar window. However, that window presents active applications in your system, and those active applications might include ones without a window. In most cases, you won&amp;rsquo;t care about them, but it is annoying. I typically only have a few windows opened when I&amp;rsquo;m working on mac. I&amp;rsquo;d like to see a clean list, instead of a whole bunch of background running applications, which makes windows switching a lot more inefficient.&lt;/p>
&lt;p>Over years, I&amp;rsquo;ve always tried to find softwares that can provide similar feature as Windows and Linux do. I found two of them work really well.&lt;/p>
&lt;h3 id="hyperswitch">HyperSwitch&lt;/h3>
&lt;p>A few years ago, I found this software called &lt;a href="https://bahoom.com/hyperswitch">HyperSwitch&lt;/a>, and it does exactly what I want. It works pretty well, just like Windows and Linux.&lt;/p>
&lt;p>HyperSwitch has been pretty reliable before I switched to use the M1 macbook air from my x86 based macbook pro. On M1, I&amp;rsquo;ve encounter cases where HyperSwitch just failed to find certain windows. I&amp;rsquo;m not sure why that happened. I also noticed that HyperSwitch is not under active maintenance anymore. The last time it was updated was before 2021. Then I started to search for other solutions.&lt;/p>
&lt;h3 id="alttab">AltTab&lt;/h3>
&lt;p>&lt;a href="https://alt-tab-macos.netlify.app/">AltTab&lt;/a> is another software that provides similar feature. It pretty much does the same thing, but it has richer features. AltTab is also opened sourced at &lt;a href="https://github.com/lwouis/alt-tab-macos">github&lt;/a>.&lt;/p>
&lt;p>I started to use AltTab ever since 2021. It works pretty good, and I&amp;rsquo;m still using it now. &lt;strong>&lt;a href="https://alt-tab-macos.netlify.app/">AltTab&lt;/a> is definitely my recommended solution to mac window switching.&lt;/strong>&lt;/p>
&lt;h2 id="window-placement">Window Placement&lt;/h2>
&lt;p>Another big topic of window management is window placement. If you&amp;rsquo;ve used Windows before, you&amp;rsquo;ll noticed it provides a few pretty handy window management hotkeys:&lt;/p>
&lt;ul>
&lt;li>&lt;code>win + left&lt;/code>: Place window on the left part of the screen.&lt;/li>
&lt;li>&lt;code>win + right&lt;/code>: Place window on the right part of the screen.&lt;/li>
&lt;li>&lt;code>win + up&lt;/code>: Maximize the window.&lt;/li>
&lt;li>&lt;code>win + down&lt;/code>: Restore the previous window position if it is currently maximized, otherwise, minimize the window.&lt;/li>
&lt;/ul>
&lt;p>I wish mac had come with these features by default, but it had never came. Then I started to search solutions for that.&lt;/p>
&lt;p>There are tools to do that, e.g., &lt;a href="https://apps.apple.com/app/id441258766">Magnet&lt;/a>, &lt;a href="https://apps.apple.com/app/bettersnaptool/id417375580">BetterSnapTool&lt;/a>, &lt;a href="https://hazeover.com/">HazeOver&lt;/a>, etc. But as a software engineer, I definitely can&amp;rsquo;t accept these solutions, because they not customizable enough.&lt;/p>
&lt;p>And then I found &lt;a href="https://www.hammerspoon.org/">hammerspoon&lt;/a>, which is truely a game changer on mac. Hammerspoon is not a window management tool. Instead, it is a powerful automation framework on mac. Hammerspoon connects a lot of system APIs with lua script. In other words, you can use lua script to call system APIs to implement your feature.&lt;/p>
&lt;p>This is a great tool for us to define some window management tricks. Based on that, I wrote a script to help me place windows. It is similar to the one on Windows and Linux, but there is a little bit difference.&lt;/p>
&lt;ul>
&lt;li>&lt;code>win + left&lt;/code>: Place window on the left part of the screen. If your window is already on the left side of the screen, it will be moved to the monitor to the left (if there is any).&lt;/li>
&lt;li>&lt;code>win + right&lt;/code>: Place window on the right part of the screen. If your window is already on the right side of the screen, it will be moved to the monitor to the right (if there is any).&lt;/li>
&lt;li>&lt;code>win + up&lt;/code>: Maximize the window.&lt;/li>
&lt;li>&lt;code>win + down&lt;/code>: Place the window in the center.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Window placement with hammerspoon is definitely the most preferred solution to me&lt;/strong>. I attached the script on the bottom. To use it, install hammerspoon (follow this &lt;a href="https://www.hammerspoon.org/">page&lt;/a>) first, then copy the following content to &lt;code>~/.hammerspoon/init.lua&lt;/code>, and restart hammerspoon.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-lua" data-lang="lua">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">------------------------------------------------------------------------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">-- The following script comes from https://devmemo.gitlab.io/blog/mac_window_management/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">------------------------------------------------------------------------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">local&lt;/span> devmemo_config &lt;span style="color:#f92672">=&lt;/span> { }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">------------------------------------------------------------------------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">-- Window Management&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">------------------------------------------------------------------------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">function&lt;/span> &lt;span style="color:#a6e22e">devmemo_config&lt;/span>.&lt;span style="color:#a6e22e">focused_window&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> hs.window.focusedWindow()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">function&lt;/span> &lt;span style="color:#a6e22e">devmemo_config&lt;/span>.&lt;span style="color:#a6e22e">set_frame&lt;/span>(unit)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> devmemo_config.focused_window():setFrame(unit, &lt;span style="color:#ae81ff">0&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">function&lt;/span> &lt;span style="color:#a6e22e">devmemo_config&lt;/span>.&lt;span style="color:#a6e22e">maximize_window&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">local&lt;/span> sf &lt;span style="color:#f92672">=&lt;/span> devmemo_config.focused_window():screen():frame()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> devmemo_config.set_frame({ x &lt;span style="color:#f92672">=&lt;/span> sf.x, y &lt;span style="color:#f92672">=&lt;/span> sf.y, w &lt;span style="color:#f92672">=&lt;/span> sf.w, h &lt;span style="color:#f92672">=&lt;/span> sf.h })
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">function&lt;/span> &lt;span style="color:#a6e22e">devmemo_config&lt;/span>.&lt;span style="color:#a6e22e">center_window&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">local&lt;/span> sf &lt;span style="color:#f92672">=&lt;/span> devmemo_config.focused_window():screen():frame()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> devmemo_config.set_frame({ x &lt;span style="color:#f92672">=&lt;/span> sf.x &lt;span style="color:#f92672">+&lt;/span> sf.w &lt;span style="color:#f92672">/&lt;/span> &lt;span style="color:#ae81ff">4&lt;/span>, y &lt;span style="color:#f92672">=&lt;/span> sf.y &lt;span style="color:#f92672">+&lt;/span> sf.h &lt;span style="color:#f92672">/&lt;/span> &lt;span style="color:#ae81ff">4&lt;/span>, w &lt;span style="color:#f92672">=&lt;/span> sf.w &lt;span style="color:#f92672">/&lt;/span> &lt;span style="color:#ae81ff">2&lt;/span>, h &lt;span style="color:#f92672">=&lt;/span> sf.h &lt;span style="color:#f92672">/&lt;/span> &lt;span style="color:#ae81ff">2&lt;/span> })
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">function&lt;/span> &lt;span style="color:#a6e22e">devmemo_config&lt;/span>.&lt;span style="color:#a6e22e">send_window_left&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">local&lt;/span> sf &lt;span style="color:#f92672">=&lt;/span> devmemo_config.focused_window():screen():frame()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">local&lt;/span> old_wf &lt;span style="color:#f92672">=&lt;/span> devmemo_config.focused_window():frame()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> devmemo_config.set_frame({ x &lt;span style="color:#f92672">=&lt;/span> sf.x, y &lt;span style="color:#f92672">=&lt;/span> sf.y, w &lt;span style="color:#f92672">=&lt;/span> sf.w &lt;span style="color:#f92672">/&lt;/span> &lt;span style="color:#ae81ff">2&lt;/span>, h &lt;span style="color:#f92672">=&lt;/span> sf.h })
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> old_wf &lt;span style="color:#f92672">==&lt;/span> devmemo_config.focused_window():frame() &lt;span style="color:#66d9ef">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">local&lt;/span> west_screen &lt;span style="color:#f92672">=&lt;/span> devmemo_config.focused_window():screen():toWest()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> west_screen &lt;span style="color:#f92672">~=&lt;/span> &lt;span style="color:#66d9ef">nil&lt;/span> &lt;span style="color:#66d9ef">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">local&lt;/span> wsf &lt;span style="color:#f92672">=&lt;/span> west_screen:frame()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> devmemo_config.set_frame({ x &lt;span style="color:#f92672">=&lt;/span> wsf.x &lt;span style="color:#f92672">+&lt;/span> wsf.w &lt;span style="color:#f92672">/&lt;/span> &lt;span style="color:#ae81ff">2&lt;/span>, y &lt;span style="color:#f92672">=&lt;/span> wsf.y, w &lt;span style="color:#f92672">=&lt;/span> wsf.w &lt;span style="color:#f92672">/&lt;/span> &lt;span style="color:#ae81ff">2&lt;/span>, h &lt;span style="color:#f92672">=&lt;/span> wsf.h })
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">function&lt;/span> &lt;span style="color:#a6e22e">devmemo_config&lt;/span>.&lt;span style="color:#a6e22e">send_window_right&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">local&lt;/span> sf &lt;span style="color:#f92672">=&lt;/span> devmemo_config.focused_window():screen():frame()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">local&lt;/span> old_wf &lt;span style="color:#f92672">=&lt;/span> devmemo_config.focused_window():frame()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> devmemo_config.set_frame({ x &lt;span style="color:#f92672">=&lt;/span> sf.x &lt;span style="color:#f92672">+&lt;/span> sf.w &lt;span style="color:#f92672">/&lt;/span> &lt;span style="color:#ae81ff">2&lt;/span>, y &lt;span style="color:#f92672">=&lt;/span> sf.y, w &lt;span style="color:#f92672">=&lt;/span> sf.w &lt;span style="color:#f92672">/&lt;/span> &lt;span style="color:#ae81ff">2&lt;/span>, h &lt;span style="color:#f92672">=&lt;/span> sf.h })
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> old_wf &lt;span style="color:#f92672">==&lt;/span> devmemo_config.focused_window():frame() &lt;span style="color:#66d9ef">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">local&lt;/span> east_screen &lt;span style="color:#f92672">=&lt;/span> devmemo_config.focused_window():screen():toEast()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> east_screen &lt;span style="color:#f92672">~=&lt;/span> &lt;span style="color:#66d9ef">nil&lt;/span> &lt;span style="color:#66d9ef">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">local&lt;/span> esf &lt;span style="color:#f92672">=&lt;/span> east_screen:frame()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> devmemo_config.set_frame({ x &lt;span style="color:#f92672">=&lt;/span> esf.x, y &lt;span style="color:#f92672">=&lt;/span> esf.y, w &lt;span style="color:#f92672">=&lt;/span> esf.w &lt;span style="color:#f92672">/&lt;/span> &lt;span style="color:#ae81ff">2&lt;/span>, h &lt;span style="color:#f92672">=&lt;/span> esf.h })
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">------------------------------------------------------------------------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">-- Key Bindings and Taps&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">------------------------------------------------------------------------------------------&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>hs.hotkey.bind({&lt;span style="color:#e6db74">&amp;#34;cmd&amp;#34;&lt;/span>}, &lt;span style="color:#e6db74">&amp;#34;Left&amp;#34;&lt;/span>, &lt;span style="color:#66d9ef">function&lt;/span>() devmemo_config.send_window_left() &lt;span style="color:#66d9ef">end&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>hs.hotkey.bind({&lt;span style="color:#e6db74">&amp;#34;cmd&amp;#34;&lt;/span>}, &lt;span style="color:#e6db74">&amp;#34;Right&amp;#34;&lt;/span>, &lt;span style="color:#66d9ef">function&lt;/span>() devmemo_config.send_window_right() &lt;span style="color:#66d9ef">end&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>hs.hotkey.bind({&lt;span style="color:#e6db74">&amp;#34;cmd&amp;#34;&lt;/span>}, &lt;span style="color:#e6db74">&amp;#34;Up&amp;#34;&lt;/span>, &lt;span style="color:#66d9ef">function&lt;/span>() devmemo_config.maximize_window() &lt;span style="color:#66d9ef">end&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>hs.hotkey.bind({&lt;span style="color:#e6db74">&amp;#34;cmd&amp;#34;&lt;/span>}, &lt;span style="color:#e6db74">&amp;#34;Down&amp;#34;&lt;/span>, &lt;span style="color:#66d9ef">function&lt;/span>() devmemo_config.center_window() &lt;span style="color:#66d9ef">end&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item></channel></rss>