<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Nick Charlton</title>
    <link href="https://nickcharlton.net/atom.xml" rel="self" />
    <link href="https://nickcharlton.net" />
    <id>https://nickcharlton.net/atom.xml</id>
    <author>
        <name>Nick Charlton</name>
        <email>nick@nickcharlton.net</email>
    </author>
    <updated>Wed, 22 Apr 2026 16:46:13 +0000</updated>
    
    <entry>
        <title>Prometheus Remote Write Examples</title>
        <link href="https://nickcharlton.net/posts/prometheus-remote-writes-examples.html" />
        <id>https://nickcharlton.net/posts/prometheus-remote-writes-examples.html</id>
        <published>Wed, 22 Apr 2026 00:00:00 +0000</published>
        <updated>Wed, 22 Apr 2026 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Last year, I worked on a project that sends telemetry data to &lt;a href=&quot;https://grafana.com/products/cloud/&quot;&gt;Grafana
Cloud&lt;/a&gt;. It was data from remote sensors (i.e.: internet of things) and my
theory was that having the data end up in Prometheus would be ideal for data
storage, software licensing and general support reasons as then it’d be
possible to query the sensor data like any other application metrics.&lt;/p&gt;

&lt;p&gt;In practice this worked out pretty well, and I’m really happy with how it
worked out. &lt;em&gt;But&lt;/em&gt;, with how &lt;a href=&quot;https://github.com/discourse/prometheus_exporter&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prometheus_exporter&lt;/code&gt;&lt;/a&gt; ends up exposing the
metrics, you end up with a situation where the metric data persists. Instead of
the data being a point in time, the value stays constant between scrapes.
Whilst this is inherent to the design, it means that unless the whole
application is restarted (or goes down), you lose any gaps in the data which is
itself a helpful piece of information that you can alert on. For a specific
example: if temperature data is sent every 5 minutes at 21ºC, unless that value
changes, the scraped data will always report 21ºC from that point onwards.&lt;/p&gt;

&lt;p&gt;This can be solved by &lt;em&gt;pushing&lt;/em&gt; rather than &lt;em&gt;pulling&lt;/em&gt; metrics, but is something
that Prometheus tries to steer you away from doing. There is a solution to this
in the &lt;a href=&quot;https://prometheus.io/docs/instrumenting/pushing/&quot;&gt;push gateway&lt;/a&gt;, but in this situation I couldn’t use it (without
increasing the infrastructure complexity). Prometheus also supports &lt;a href=&quot;https://prometheus.io/docs/specs/prw/remote_write_spec/&quot;&gt;remote
write&lt;/a&gt;; intended for other scrapers to send data to another Prometheus.&lt;/p&gt;

&lt;p&gt;There’s a specification for remote write is supposed to work, but if you look
through the various client libraries, this isn’t implemented (usually
deliberately to stop you using it) and I couldn’t find any helpful examples of
how to use it. Whilst I never got time to implement it on the project it was
intended for, I did end up writing some examples of implementing Remote Write,
and &lt;a href=&quot;https://github.com/nickcharlton/prometheus-remote-write&quot;&gt;I’ve collected these together in a repository in case it’s useful for
others&lt;/a&gt;.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Using a Vodafone Mobile Broadband (ZTE K5161z) 4G USB dongle on Linux</title>
        <link href="https://nickcharlton.net/posts/vodafone-mobile-broadband-zte-k5161z-lte-dongle-linux.html" />
        <id>https://nickcharlton.net/posts/vodafone-mobile-broadband-zte-k5161z-lte-dongle-linux.html</id>
        <published>Wed, 11 Mar 2026 00:00:00 +0000</published>
        <updated>Wed, 11 Mar 2026 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;There are plenty of these little devices around, and &lt;a href=&quot;https://www.amazon.co.uk/dp/B0C7WFJVLN?th=1&quot;&gt;as of the start of 2026
they sell for £25 on Amazon&lt;/a&gt; unlocked for any network. I couldn’t figure out
how to get the thing to actually work though on an otherwise failing boring
Linux install.&lt;/p&gt;

&lt;p&gt;It seems likely that the current glut of them is either because of customer
returns from Vodafone, or just unused devices as 5G dongles are rolled out. But
they work perfectly well still.&lt;/p&gt;

&lt;p&gt;USB 4G devices like this are odd little beasts: this, and several other models
known on the market, like some Huawei models, are effectively a stick shaped
router which shows up as a combined mass storage device and a USB ethernet
device. On insertion, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dmesg&lt;/code&gt; tells us:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[Feb 5 14:53] usb 1-2: new high-speed USB device number 6 using xhci-hcd
[  +0.139388] usb 1-2: New USB device found, idVendor=19d2, idProduct=1225, bcdDevice=58.13
[  +0.000010] usb 1-2: New USB device strings: Mfr=2, Product=4, SerialNumber=5
[  +0.000003] usb 1-2: Product: Vodafone Mobile Broadband
[  +0.000003] usb 1-2: Manufacturer: Vodafone,Incorporated
[  +0.000003] usb 1-2: SerialNumber: 1234567890ABCDEF
[  +0.005580] usb-storage 1-2:1.0: USB Mass Storage device detected
[  +0.000188] usb-storage 1-2:1.0: Quirks match for vid 19d2 pid 1225: 1
[  +0.000042] scsi host0: usb-storage 1-2:1.0
[  +3.670038] usb 1-2: USB disconnect, device number 6
[  +0.380744] usb 1-2: new high-speed USB device number 7 using xhci-hcd
[  +0.143507] usb 1-2: New USB device found, idVendor=19d2, idProduct=1405, bcdDevice=58.13
[  +0.000007] usb 1-2: New USB device strings: Mfr=2, Product=4, SerialNumber=5
[  +0.000003] usb 1-2: Product: Vodafone Mobile Broadband
[  +0.000002] usb 1-2: Manufacturer: Vodafone,Incorporated
[  +0.000002] usb 1-2: SerialNumber: 1234567890ABCDEF
[  +0.201730] cdc_ether 1-2:1.0 eth1: register &apos;cdc_ether&apos; at usb-xhci-hcd.0-2, ZTE CDC Ethernet Device, 34:4b:50:00:00:00
[  +0.000957] usb-storage 1-2:1.2: USB Mass Storage device detected
[  +0.000385] scsi host0: usb-storage 1-2:1.2
[  +1.010171] scsi 0:0:0:0: CD-ROM            Vodafon  USB SCSI CD-ROM  2.3  PQ: 0 ANSI: 2
[  +0.000567] sr 0:0:0:0: Power-on or device reset occurred
[  +0.002185] sr 0:0:0:0: [sr0] scsi-1 drive
[  +0.002111] sr 0:0:0:0: Attached scsi CD-ROM sr0
[  +0.000366] sr 0:0:0:0: Attached scsi generic sg0 type 5
[  +0.030690] scsi 0:0:0:1: Direct-Access     Vodafon  MMC Storage      2.3  PQ: 0 ANSI: 2
[  +0.003430] sd 0:0:0:1: Attached scsi generic sg1 type 0
[  +0.000233] sd 0:0:0:1: Power-on or device reset occurred
[  +0.000377] sd 0:0:0:1: [sda] Media removed, stopped polling
[  +0.000307] sd 0:0:0:1: [sda] Attached SCSI removable disk
[  +0.371026] ISO 9660 Extensions: Microsoft Joliet Level 1
[  +0.000250] ISOFS: changing to secondary root
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The mass storage device provides a set of (unnecessary) drivers, and also
supports the Micro SD card if you wanted to use it.&lt;/p&gt;

&lt;p&gt;Assuming you’ve got a SIM card installed, it should just come up and provide
internet access. The light starts off as red and once it gets a signal (this
does take some time), will change to blue. If not, you might need to bring up
the new interface, e.g.:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;ip &lt;span class=&quot;nb&quot;&gt;link &lt;/span&gt;up eth1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The interface will then get an IP from the dongle. You can access the
configuration interface by going to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.1&lt;/code&gt; on whatever the IP range you got
assigned (maybe &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;192.168.0.*&lt;/code&gt;, maybe &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;192.168.6.*&lt;/code&gt;) and change some settings,
read any SMS messages, etc. Once configured, I’ve found it comes back up when
rebooting.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/vodafone-lte-usb-dongle-interface.png&quot; alt=&quot;A screenshot showing Vodafone USB LTE dongle interface&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Vodafone USB LTE dongle interface&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Hopefully this solves someone else spending ages going around in circles trying
to figure out how you might use them, especially if like me, it didn’t come up
automatically. Unfortunately it was much easier to find the &lt;em&gt;wrong&lt;/em&gt; answer to
how this particular device works than the correct one.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Working with Raspberry Pi images</title>
        <link href="https://nickcharlton.net/posts/working-with-raspberry-pi-images.html" />
        <id>https://nickcharlton.net/posts/working-with-raspberry-pi-images.html</id>
        <published>Tue, 10 Mar 2026 00:00:00 +0000</published>
        <updated>Tue, 10 Mar 2026 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I’ve found myself working with the &lt;a href=&quot;https://www.raspberrypi.com&quot;&gt;Raspberry Pi&lt;/a&gt; frequently recently. It’s
a nice platform, and I appreciate the software tooling for making it easy to
get going and allowing you to focus on what you’re trying to make, rather than
the embedded platform itself. But every guide points to using the &lt;a href=&quot;https://www.raspberrypi.com/documentation/computers/getting-started.html#raspberry-pi-imager&quot;&gt;Raspberry Pi
Imager&lt;/a&gt;, which isn’t a satisfying answer for what the process actually is,
which would help for figuring out how to automate the process.&lt;/p&gt;

&lt;h2 id=&quot;dd-is-always-our-friend&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dd&lt;/code&gt; is always our friend&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.raspberrypi.com/software/operating-systems/&quot;&gt;Raspberry Pi OS&lt;/a&gt;, their Debian-based distribution, is available as a
compressed image file, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.img.xz&lt;/code&gt;. Find the device (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fdisk -l&lt;/code&gt;), then we can
write the image:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;unxz 2025-12-04-raspios-trixie-arm64-lite.img.xz
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo dd &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2025-12-04-raspios-trixie-arm64-lite.img &lt;span class=&quot;nv&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/dev/sda
5832704+0 records &lt;span class=&quot;k&quot;&gt;in
&lt;/span&gt;5832704+0 records out
2986344448 bytes &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;3.0 GB, 2.8 GiB&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; copied, 268.014 s, 11.1 MB/s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ve been skipping this step for some projects and &lt;a href=&quot;https://thepihut.com/products/noobs-preinstalled-sd-card&quot;&gt;using a full image by
buying a pre-installed SD card&lt;/a&gt;, which is a helpful option.&lt;/p&gt;

&lt;h2 id=&quot;customisation&quot;&gt;Customisation&lt;/h2&gt;

&lt;p&gt;The disk image is made up of two partitions: a smaller FAT32 partition which is
mounted at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/boot&lt;/code&gt; and a larger Linux partition which holds the operating
system image:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;fdisk &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; ... snip ... &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
Disk /dev/sda: 58.94 GiB, 63281561600 bytes, 123596800 sectors
Disk model: SD/MMC
Units: sectors of 1 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; 512 &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 512 bytes
Sector size &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;logical/physical&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: 512 bytes / 512 bytes
I/O size &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;minimum/optimal&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: 512 bytes / 512 bytes
Disklabel &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;: dos
Disk identifier: 0xffc763c1

Device     Boot   Start     End Sectors  Size Id Type
/dev/sda1         16384 1064959 1048576  512M  c W95 FAT32 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;LBA&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
/dev/sda2       1064960 5832703 4767744  2.3G 83 Linux
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/boot&lt;/code&gt; partition contains files like the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.txt&lt;/code&gt;, used to configure
hardware options, plus the binaries and relevant device trees for booting the
operating system. In addition, &lt;a href=&quot;https://www.raspberrypi.com/news/cloud-init-on-raspberry-pi-os/&quot;&gt;at the end of 2025, Raspberry Pi released
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-init&lt;/code&gt; support&lt;/a&gt;. This allows us to drop a couple of YAML files onto
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/boot&lt;/code&gt; partition of the image, which are read and processed on boot.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;mount /dev/sda1 /media/usb-drive
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ls&lt;/span&gt; /media/usb-drive
bcm2710-rpi-2-b.dtb	  bcm2711-rpi-400.dtb	  bcm2712-rpi-500.dtb	      cmdline.txt   fixup.dat	     kernel8.img       start4.elf
bcm2710-rpi-3-b.dtb	  bcm2711-rpi-4-b.dtb	  bcm2712-rpi-5-b.dtb	      config.txt    fixup_db.dat     LICENCE.broadcom  start4x.elf
bcm2710-rpi-3-b-plus.dtb  bcm2711-rpi-cm4.dtb	  bcm2712-rpi-cm5-cm4io.dtb   fixup4cd.dat  fixup_x.dat      meta-data	       start_cd.elf
bcm2710-rpi-cm0.dtb	  bcm2711-rpi-cm4-io.dtb  bcm2712-rpi-cm5-cm5io.dtb   fixup4.dat    initramfs_2712   network-config    start_db.elf
bcm2710-rpi-cm3.dtb	  bcm2711-rpi-cm4s.dtb	  bcm2712-rpi-cm5l-cm4io.dtb  fixup4db.dat  initramfs8	     overlays	       start.elf
bcm2710-rpi-zero-2.dtb	  bcm2712d0-rpi-5-b.dtb   bcm2712-rpi-cm5l-cm5io.dtb  fixup4x.dat   issue.txt	     start4cd.elf      start_x.elf
bcm2710-rpi-zero-2-w.dtb  bcm2712-d-rpi-5-b.dtb   bootcode.bin		      fixup_cd.dat  kernel_2712.img  start4db.elf      user-data
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The Pi can be configured like any other using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-init&lt;/code&gt;, but &lt;a href=&quot;https://docs.cloud-init.io/en/latest/reference/modules.html#raspberry-pi-configuration&quot;&gt;it’s also got
a Raspberry Pi specific module which is handy for configuring hardware&lt;/a&gt;,
including &lt;a href=&quot;https://github.com/raspberrypi/rpi-usb-gadget&quot;&gt;support for the fairly new USB gadget mode&lt;/a&gt;. Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-init&lt;/code&gt;
makes for a nice path for future automation.&lt;/p&gt;

&lt;p&gt;I’m bootstrapping Pis with something that looks like this as the baseline for
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user-data&lt;/code&gt; (I’m leaving &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;meta-data&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;network-config&lt;/code&gt; the same as the
default behaviour is fine):&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#cloud-config&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;hostname&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;test-pi&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;manage_etc_hosts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Europe/London&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;pi&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;users,adm,dialout,audio,netdev,video,plugdev,cdrom,games,input,gpio,spi,i2c,render,sudo&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/bin/bash&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;lock_passwd&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;passwd&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$6$rounds=4096$MwcQMjo1p9YjlEBx$NkyMyUaGVa8hdD33aO/9XS/NoHdJ1a/ekfXOaF2QbYAWyt8eC7weNTAu9N2aS1Uk4zj1BNnD/tRr19nNoe/190&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ssh_authorized_keys&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFypAVlkyYVwdFAUrDnAcZqIM6Lkusv+9J3rRUn7qZzA&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;sudo&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ALL=(ALL) NOPASSWD:ALL&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;rpi&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;enable_usb_gadget&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;enable_ssh&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The password used above is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;password&lt;/code&gt;, but generated using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mkpasswd
--method=SHA-512 --rounds=4096&lt;/code&gt;, &lt;a href=&quot;https://docs.cloud-init.io/en/latest/reference/examples.html#yaml-examples&quot;&gt;as recommended by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-init&lt;/code&gt;&lt;/a&gt;. If you
set a password, you also need to set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lock_passwd: false&lt;/code&gt;, &lt;a href=&quot;https://raspberrypi.stackexchange.com/a/123700&quot;&gt;otherwise you can
never login&lt;/a&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enable_ssh: true&lt;/code&gt; is a Raspberry Pi-specific option, as
otherwise SSH isn’t up when it first boots.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Combining secret values in files on Kubernetes</title>
        <link href="https://nickcharlton.net/posts/combining-secret-values-in-files-on-kubernetes.html" />
        <id>https://nickcharlton.net/posts/combining-secret-values-in-files-on-kubernetes.html</id>
        <published>Mon, 09 Mar 2026 00:00:00 +0000</published>
        <updated>Mon, 09 Mar 2026 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;There’s a few applications which make handling their associated secrets and
configuration particularly tricky to do on Kubernetes, because the main
configuration file also has many secrets in it.  We don’t want to end up in a
position where the secrets are, or could easily be, left in a code repository,
but we also don’t necessarily want to put all of our configuration into a
secret that makes it hard to track changes to the configuration over time.&lt;/p&gt;

&lt;p&gt;If we store our configuration in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConfigMap&lt;/code&gt;, and our secrets in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Secret&lt;/code&gt;,
we can have a file out of the configuration, and the secrets as environment
variables. Unix has a solution to combining the two: &lt;a href=&quot;https://linux.die.net/man/1/envsubst&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;envsubst&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hadn’t seen anyone handle secrets like that on Kubernetes, so I thought I’d
give it a go. It worked pretty well, but it is a little cursed.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConfigMap&lt;/code&gt; is fairly conventional:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ConfigMap&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;config&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;combined-secrets&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;config&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;config.yaml.template&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;---&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;plain_value: Hello world!&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;secret_value: $TOP_SECRET_VALUE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secret_value&lt;/code&gt;, we have a placeholder which will be replaced by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;envsubst&lt;/code&gt;.
Then we need a &lt;a href=&quot;https://kubernetes.io/docs/tasks/configmap-secret/managing-secret-using-config-file/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Secret&lt;/code&gt;, which here is directly with a file&lt;/a&gt; as an example:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Secret&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;secrets&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;combined-secrets&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;config&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Opaque&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;stringData&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;TOP_SECRET_VALUE&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;I am very secretive.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The values are in all caps, because all of the values will be directly mounted
as environment variables later.&lt;/p&gt;

&lt;p&gt;Then, our example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Deployment&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apps/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Deployment&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;deployment&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;combined-secrets&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;deployment&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;deployment&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;deployment&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;initContainers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;build-config&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/bin/sh&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-c&quot;&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;&amp;gt;&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;apt-get update -q; apt-get install -yq gettext-base;&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;envsubst &amp;lt; /config.yaml.template &amp;gt; /data/config.yaml&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;debian&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;envFrom&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;secretRef&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;secrets&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;volumeMounts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;shared-files&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;mountPath&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/data&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;config-template&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;mountPath&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/config.yaml.template&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;subPath&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;config.yaml.template&lt;/span&gt;

      &lt;span class=&quot;na&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;debian&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;debian&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tail&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-f&quot;&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/dev/null&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;volumeMounts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;shared-files&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;mountPath&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/data&lt;/span&gt;

      &lt;span class=&quot;na&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;shared-files&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;emptyDir&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{}&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;config-template&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;configMap&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;config&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This uses a couple of tricks to pull this off:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;We use an &lt;a href=&quot;https://kubernetes.io/blog/2025/04/22/multi-container-pods-overview/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initContainer&lt;/code&gt;&lt;/a&gt; to prepare our configuration before starting
the main container,&lt;/li&gt;
  &lt;li&gt;To store the result, we use &lt;a href=&quot;https://kubernetes.io/docs/concepts/storage/volumes/#emptydir&quot;&gt;a volume that’s shared among the Pod&lt;/a&gt; as
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/data&lt;/code&gt;,&lt;/li&gt;
  &lt;li&gt;The &lt;a href=&quot;https://hub.docker.com/_/debian&quot;&gt;Debian&lt;/a&gt; container image unfortunately doesn’t include &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;envsubst&lt;/code&gt;, so
we need to install that ourselves,&lt;/li&gt;
  &lt;li&gt;We also need to &lt;a href=&quot;https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#run-a-command-in-a-shell&quot;&gt;run the commands as a shell for the redirection to
work&lt;/a&gt;,&lt;/li&gt;
  &lt;li&gt;To truly cement it’s cursed nature: we use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;gt;&lt;/code&gt; which doesn’t retain
line breaks for what’s actually a one-liner,&lt;/li&gt;
  &lt;li&gt;Finally, for the main container we &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tail&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/null&lt;/code&gt; which is just a trick
to stop the container exiting whilst we use it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If we connect to the deployment container, we can see the resulting combined
file:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ kubectl -n combined-secrets exec -it deployment-568b66968-z9t77 -- /bin/bash
Defaulted container &quot;debian&quot; out of: debian, build-config (init)
root@deployment-568b66968-z9t77:/# cat /data/config.yaml
---
plain_value: Hello world!
secret_value: I am very secretive.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’s unfortunate that we need to install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gettext&lt;/code&gt; for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;envsubst&lt;/code&gt; (it’s a good
opportunity for a custom image), but this works well.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Static files with Nginx, Docker &amp; Kubernetes</title>
        <link href="https://nickcharlton.net/posts/static-files-nginx-docker-kubernetes.html" />
        <id>https://nickcharlton.net/posts/static-files-nginx-docker-kubernetes.html</id>
        <published>Sun, 18 May 2025 00:00:00 +0000</published>
        <updated>Sun, 18 May 2025 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I wanted a way to serve up a set of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.well-known&lt;/code&gt; paths, as easily as I could
get away with, but keep it all alongside some other Kubernetes manifests.&lt;/p&gt;

&lt;p&gt;I hoped it’d be something I could trick the &lt;a href=&quot;https://kubernetes.github.io/ingress-nginx/&quot;&gt;Nginx Ingress controller&lt;/a&gt; into
doing (as that already exists), perhaps with just a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConfigMap&lt;/code&gt; but it’s not
really designed to do that. I didn’t think it should be necessary to build a
custom container image either. But we could do it with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Deployment&lt;/code&gt; and an
Nginx container. There’s a bit to it, but perhaps it’s helpful for others.&lt;/p&gt;

&lt;p&gt;I’m going to use &lt;a href=&quot;https://spec.matrix.org/v1.14/client-server-api/#well-known-uri&quot;&gt;Matrix’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.well-known&lt;/code&gt; paths&lt;/a&gt; here, but the same principle
could apply to anything static.&lt;/p&gt;

&lt;h2 id=&quot;a-quick-experiment-with-docker&quot;&gt;A quick experiment with Docker&lt;/h2&gt;

&lt;p&gt;To start with, we need something to serve:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.well-known/matrix/server&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;m.server&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;matrix.nickcharlton.net:443&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can then serve it by mounting the current directory in the right place,
using the &lt;a href=&quot;https://hub.docker.com/_/nginx&quot;&gt;Nginx Docker image&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 8080:80 &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; .:/usr/share/nginx/html:ro nginx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And test it with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;curl http://localhost:8080/.well-known/matrix/server
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;m.server&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;matrix.nickcharlton.net:443&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That works quite nicely, which is just enough experimenting to turn it into
Kubernetes manifests.&lt;/p&gt;

&lt;h2 id=&quot;running-on-kubernetes&quot;&gt;Running on Kubernetes&lt;/h2&gt;

&lt;p&gt;To start with, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConfigMap&lt;/code&gt; to hold the data:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# configmap-well-known.yaml&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ConfigMap&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;well-known&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;well-known&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&quot;m.server&quot;: &quot;matrix.nickcharlton.net:443&quot;&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Deployment&lt;/code&gt; to run Nginx:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# deployment.yaml&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apps/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Deployment&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;well-known&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;well-known&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;well-known&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;well-known&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nginx&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nginx&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;containerPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TCP&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;volumeMounts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;data&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;mountPath&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/usr/share/nginx/html/.well-known/matrix/server&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;subPath&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;server&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;data&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;configMap&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;well-known&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, a Service and Ingress:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# service.yaml&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Service&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;well-known&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;well-known&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# type: LoadBalancer&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;clusterIP&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;None&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;targetPort&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;well-known&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ingress.yaml&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Ingress&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;well-known&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ingressClassName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nginx&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nickcharlton.net&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;pathType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Prefix&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;backend&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;well-known&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I use &lt;a href=&quot;https://kube-vip.io/&quot;&gt;kube-vip&lt;/a&gt; on my clusters, so I could use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LoadBalancer&lt;/code&gt;
service here to get an IP outside of the cluster and use that (a pattern you
can use elsewhere too). But, it’s also possible to use a headless service with
an Ingress, which is what’s done here.&lt;/p&gt;

&lt;p&gt;We can test via the Ingress directly by forwarding a port:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;kubectl port-forward &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;ingress-nginx service/ingress-nginx-controller 8080:80
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;--resolve&lt;/span&gt; nickcharlton.net:8080:127.0.0.1 http://nickcharlton.net:8080/.well-known/matrix/server
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;m.server&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;matrix.nickcharlton.net:443&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or, if we can access outside the cluster (assuming the domain resolves):&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;curl http://nickcharlton.net:8080/.well-known/matrix/server
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;m.server&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;matrix.nickcharlton.net:443&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</summary>
    </entry>
    
    <entry>
        <title>Two sides and four columns of A5: or a technique to help with too many projects</title>
        <link href="https://nickcharlton.net/posts/two-sides-and-four-columns-of-5-a-technique-to-help-with-too-many-projects.html" />
        <id>https://nickcharlton.net/posts/two-sides-and-four-columns-of-5-a-technique-to-help-with-too-many-projects.html</id>
        <published>Fri, 10 Jan 2025 00:00:00 +0000</published>
        <updated>Fri, 10 Jan 2025 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;2024 sucked for various reasons, but when I’m not happy (over a long period of
time especially), I find I start far more projects than I get close to
finishing. I end up having far too many things in progress, where I’m
(consciously, or unconsciously) trying to work on multiple things at the same
time, continually thinking about what I’m &lt;em&gt;not&lt;/em&gt; doing, being stuck on one
project so others can’t proceed, or missing out on new and other interesting
things because I’m too busy and stressed out with the amount of projects going
on.&lt;/p&gt;

&lt;p&gt;Towards the end of last year, I resolved to try and get this under control by
trying out a new idea and review process: writing down all of the projects and
their current state, then reviewing this list every two weeks. I’d ask myself
these questions:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;What projects are in progress?&lt;/li&gt;
  &lt;li&gt;What not-quite-a-project stuff is happening?&lt;/li&gt;
  &lt;li&gt;What should I kill?&lt;/li&gt;
  &lt;li&gt;Is this the right frequency to think about it?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then I’d write down all of the projects in &lt;a href=&quot;https://postalco.com/en-global/collections/snap_pads/products/スナップパッドsq-a5&quot;&gt;a fancy notepad my partner bought
back for me from a trip to Japan which has two columns on it&lt;/a&gt;. This notebook
sits to the left of my keyboard, so it’s always in sight.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/project-wip-notebook.jpeg&quot; alt=&quot;A photograph of an example of projects listed out. It shows
           two columns, because there&apos;s so many projects.&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;The notebook sits to the left of my keyboard, crossing them out whilst
  they&apos;re done.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The first time I sat down and wrote down the projects, I ended up with two bits
of paper and four whole columns on them of projects I’d started but not
completed. Just doing this the first time really emphasised the state of
things. As the time went on, I’d cross off each item (or a sub-item, until I
could cross off the whole item).&lt;/p&gt;

&lt;p&gt;Two weeks later, I returned to do it all again. Starting by looking at the
state of the current list, I’d take a new sheet and write out the projects
again. The crossed out ones would be gone, but any new projects would be added.&lt;/p&gt;

&lt;p&gt;In the first review, I was surprised by how much I’d managed to get through.
I’d spent the time getting rid of as many projects as possible. Not that
excited about it? Archive it, and move on. Last thing to get it over the finish
line? Do that first. In these first two weeks, I’d managed to get down to just
two columns, on one sheet.&lt;/p&gt;

&lt;p&gt;I continued this for several months (never getting it any smaller than two
columns), gradually crossing items out, bit by bit. Adding new projects as they
happened (but while being &lt;em&gt;very&lt;/em&gt; selective about those, to avoid making the
problem even worse and ending up back where I started). But, finally, as I
publish this, I’m down to just one column. Hopefully I can keep it that way.&lt;/p&gt;

&lt;p&gt;Maybe something similar can work for you.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Configuring a serial console on Debian 12</title>
        <link href="https://nickcharlton.net/posts/configuring-serial-console-debian-12.html" />
        <id>https://nickcharlton.net/posts/configuring-serial-console-debian-12.html</id>
        <published>Thu, 09 Jan 2025 00:00:00 +0000</published>
        <updated>Thu, 09 Jan 2025 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I’m a fan of serial consoles for out-of-band access, since having used them a
lot for network equipment. Recently, I was trying to figure out a graphics
incompatibility on a system using Wayland on Debian and wanted to set up a
serial console to try to stop part of the debugging process being so painful.&lt;/p&gt;

&lt;p&gt;Since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;systemd&lt;/code&gt; has rolled out to Debian though, there are a lot of outdated
articles, and now it’s quite a bit easier to get going. &lt;a href=&quot;https://wiki.debian.org/systemd#Virtual_and_serial_console_changes&quot;&gt;According to the
Debian Wiki&lt;/a&gt;, we can now start a service to do this on demand:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;systemctl &lt;span class=&quot;nb&quot;&gt;enable &lt;/span&gt;serial-getty@ttyS0.service
systemctl start serial-getty@ttyS0.service
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But if we configure the bootloader (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grub&lt;/code&gt;) instead, we can get serial console
output from much earlier in the boot process.&lt;/p&gt;

&lt;h2 id=&quot;determining-the-correct-serial-device&quot;&gt;Determining the correct serial device&lt;/h2&gt;

&lt;p&gt;Most of the time, you can likely assume it’s going to be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ttyS0&lt;/code&gt;, but it’s
worth checking:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dmesg | &lt;span class=&quot;nb&quot;&gt;grep tty&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;    0.066243] printk: console &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;tty0] enabled
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;    0.685110] 00:01: ttyS0 at I/O 0x3f8 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;irq &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 4, base_baud &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 115200&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; is a 16550A
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;    0.686459] 00:02: ttyS1 at I/O 0x2f8 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;irq &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 3, base_baud &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 115200&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; is a 16550A
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;   18.042511] systemd[1]: Created slice system-getty.slice - Slice /system/getty.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This motherboard has a built-in serial port, plus another is configured &lt;a href=&quot;https://www.startech.com/en-gb/cables/pnl9m16&quot;&gt;using
a header cable&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;configure-grub&quot;&gt;Configure Grub&lt;/h2&gt;

&lt;p&gt;We open up &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/default/grub&lt;/code&gt;, and modify &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GRUB_CMDLINE_LINUX&lt;/code&gt; to set a
console on the serial port, followed by updating the live Grub configuration:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo vim /etc/default/grub
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;-GRUB_CMDLINE_LINUX=&quot;&quot;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+GRUB_CMDLINE_LINUX=&quot;console=tty0 console=ttyS0&quot;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo update-grub
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By default, this will be 9600 baud. But we can configure that, for example,
setting 115200:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;GRUB_CMDLINE_LINUX=&quot;console=tty0 console=ttyS0,115200n8&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/debian-serial-console-gtkterm.png&quot; alt=&quot;A screenshot showing gtkterm, a serial console client, which
           is displaying the Debian login prompt.&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;A Debian login screen in gtkterm&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;fun-aside-firmware-console-redirection&quot;&gt;Fun aside: Firmware Console Redirection&lt;/h2&gt;

&lt;p&gt;Whilst I was configuring this board (a &lt;a href=&quot;https://www.supermicro.com/en/products/motherboard/X11SSH-LN4F&quot;&gt;Supermicro X11SSH-LN4F&lt;/a&gt;), I noticed
that it supports console redirect inside the firmware (BIOS). If we configure
the same settings from the firmware through to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grub&lt;/code&gt;, we can keep the same
console session through the whole boot process. This also works with
&lt;a href=&quot;https://freedos.org&quot;&gt;FreeDOS&lt;/a&gt;, which is handy if you’re doing a firmware upgrade.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/debian-serial-console-bios.png&quot; alt=&quot;A screenshot showing the firmware (BIOS) of a Supermicro
           motherboard, through a serial console.&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;We can configure the BIOS even inside a serial console&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;As long as you know the correct command to get into the firmware (delete, in
this case) when the screen goes blank you can just keep hitting the combination
to get into setup.&lt;/p&gt;

&lt;p&gt;Of note, I found that less than 115200 baud made the firmware incredibly slow
to redraw. It also doesn’t draw elements under the cursor, so you might need to
move around a bunch to see what’s being selected.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/debian-serial-console-redirection-one.png&quot; alt=&quot;A screenshot showing the firmware (BIOS) where COM1 Console
           Redirection has been enabled.&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;We can configure COM1 (which matches to `ttyS0`) for console redirection.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/debian-serial-console-redirection-two.png&quot; alt=&quot;A screenshot showing the firmware (BIOS) where COM1 Console
           Redirection has been configured with 115200 baud.&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;We can also configure the speed, in this case, 115200, to match Debian once
  it&apos;s booted.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/debian-serial-console-ascii-art.png&quot; alt=&quot;A screenshot showing the boot process of a Supermicro
           motherboard. It is showing some fun ASCII art of the Supermicro
           logo.&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;And get this fun ASCII art too&lt;/figcaption&gt;
&lt;/figure&gt;

</summary>
    </entry>
    
    <entry>
        <title>Installing Debian 12 on a Dell Wyse 3040 Thin Client</title>
        <link href="https://nickcharlton.net/posts/installing-debian-12-dell-wyse-3040.html" />
        <id>https://nickcharlton.net/posts/installing-debian-12-dell-wyse-3040.html</id>
        <published>Fri, 03 Jan 2025 00:00:00 +0000</published>
        <updated>Fri, 03 Jan 2025 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;The &lt;a href=&quot;https://www.parkytowers.me.uk/thin/wyse/3040/&quot;&gt;Dell Wyse 3040&lt;/a&gt; is a little thin client device that’s surprisingly
powerful and also very low power. I picked one up last year, new in box, for
about £30 for a project. Unfortunately, getting Debian running on it is not so
straightforward &lt;a href=&quot;https://www.parkytowers.me.uk/thin/wyse/3040/linux.shtml&quot;&gt;because it has a buggy EFI implementation which means that
whilst you can complete the install, you can’t boot it afterwards&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Wyse 3040 only uses UEFI and doesn’t support a legacy BIOS mode. Usually, a
boot variable is defined which points to the bootloader for each vendor’s
operating system. In Debian’s case, this should be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\EFI\debian\grubx64.efi&lt;/code&gt;.
But, the Wyse 3040 firmware is missing this. Instead, &lt;a href=&quot;https://wiki.debian.org/UEFI&quot;&gt;we need to force the
bootloader to exist at another known path — originally intended for
removable media: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\EFI\boot\bootx64.efi&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://wiki.debian.org/UEFI#Force_grub-efi_installation_to_the_removable_media_path&quot;&gt;Fortunately, there’s an option in the Debian installer&lt;/a&gt; to do this which is
much more robust than doing it ourselves (which the Debian wiki also describes)
as it could be broken with updates.&lt;/p&gt;

&lt;p&gt;I’ll be installing Debian on an otherwise empty USB flash drive to maintain the
original install, and booting from another USB drive with the latest Debian
netinst on. You can disable booting from the internal drive, if you want.
Nothing else in the firmware needs changing from the defaults.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/wyse-3040-firmware-internal-drive.png&quot; alt=&quot;A screen capture of a Dell Wyse 3040 firmware (BIOS) showing
           the boot sequence. The UEFI: Hard Drive, Partition 1 option has been
           disabled.&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;A screenshot of the EFI firmware, showing the internal drive disabled.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Some things which are helpful:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;F2 for setup&lt;/li&gt;
  &lt;li&gt;F12 for the boot menu&lt;/li&gt;
  &lt;li&gt;The default password is “Fireport”&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;installing-debian&quot;&gt;Installing Debian&lt;/h2&gt;

&lt;p&gt;Boot from the USB drive, once you’re at the Debian installer, go into &lt;em&gt;Advanced
options&lt;/em&gt; and select &lt;em&gt;Expert install&lt;/em&gt;.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/wyse-3040-debian-installer-expert-install.png&quot; alt=&quot;A screen capture from the Debian installer, showing that we&apos;re
           in Advanced options. Expert install has been selected.&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Debian installer ready to start an &quot;Expert install&quot;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;You can then proceed through each section of the install. In the &lt;em&gt;Expert
install&lt;/em&gt; mode, you’re asked many more questions, but it’s broadly the same as
the normal installer. You just have to select each stage of the installer
yourself. I didn’t need to select any additional locales or components for my
install and preselected values (for the NIC, kernel, drivers, etc) were all
fine.&lt;/p&gt;

&lt;p&gt;Eventually, you’ll proceed through the setup until you’re ready to select
&lt;em&gt;Install the GRUB boot loader&lt;/em&gt;:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/wyse-3040-debian-installer-grub.png&quot; alt=&quot;A screen capture showing the Debian installer main menu whilst
           in Expert install. Install the GRUB boot loader has been selected.&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Debian installer showing the next installation step&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Then look out for &lt;em&gt;Force extra installation to the EFI removable media path?&lt;/em&gt;.
We’ll select &lt;em&gt;Yes&lt;/em&gt;.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/wyse-3040-debian-installer-force-efi.png&quot; alt=&quot;A screen capture showing the Debian installer during
           installation of the GRUB boot loader. It is asking whether to Force
           GRUB installation to the EFI removable media path. Yes has been
           selected.&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;During the Debian install, we select &quot;yes&quot; to work around the buggy EFI
  implementation&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I also selected &lt;em&gt;No&lt;/em&gt; to &lt;em&gt;Update NVRAM variables to automatically boot into
Debian?&lt;/em&gt; to preserve the existing boot behaviour.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/wyse-3040-debian-installer-update-nvram.png&quot; alt=&quot;A screen capture showing the Debian installer during
           installation of the GRUB boot loader. It is asking whether to update
           NVRAM variables to automatically boot into Debian. No has been
           selected.&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;During the Debian install, we select &quot;no&quot; to not have Debian automatically
  boot.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Once this stage of the installation is finished, you’ll be prompted to &lt;em&gt;Finish
the installation&lt;/em&gt; and then to reboot.&lt;/p&gt;

&lt;p&gt;Debian should then successfully boot.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/wyse-3040-debian-installed.png&quot; alt=&quot;A screen capture showing the Debian login prompt after
           successfully booting.&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Finally, we can boot into a login prompt.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;You will see errors about an invalid parameter on boot, and also some Intel
firmware warnings. For the former, Grub launches soon after and for the second
this seems to be harmless (so far).&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>On the path to Administrate v1</title>
        <link href="https://nickcharlton.net/posts/path-to-administate-v1.html" />
        <id>https://nickcharlton.net/posts/path-to-administate-v1.html</id>
        <published>Wed, 27 Nov 2024 00:00:00 +0000</published>
        <updated>Wed, 27 Nov 2024 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Nearly 8 years after cutting &lt;a href=&quot;https://github.com/thoughtbot/administrate/releases/tag/v0.3.0&quot;&gt;my first release of Administrate&lt;/a&gt;, we’re
nearly at the point of releasing v1, which has been my big open source focus
all year!&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We’re close to releasing Administrate v1. We’d love it if you could try
it out!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://thoughtbot.com/blog/on-the-path-to-administrate-v1&quot;&gt;See on Giant Robots&lt;/a&gt;&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Automating barcode scanner tests with Capybara</title>
        <link href="https://nickcharlton.net/posts/automating-barcode-scanner-tests-with-capybara.html" />
        <id>https://nickcharlton.net/posts/automating-barcode-scanner-tests-with-capybara.html</id>
        <published>Tue, 26 Nov 2024 00:00:00 +0000</published>
        <updated>Tue, 26 Nov 2024 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Well over a year ago, we worked on a project that used barcode scanners.
Knowing that they’re just human interface devices (keyboards) in most cases
(and/or can be configured as such), it makes them quite easy to work with in a
typical browser-based testing framework. But putting these two things together
wasn’t very clear to most of my colleagues (and probably not to most people
either!).&lt;/p&gt;

&lt;p&gt;My colleague Silumesii took the time to write it up (we started working on this
together, so I’ve got an author credit, but it’s really all of his work getting
published!):&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Implementing barcode scanner tests using Capybara.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://thoughtbot.com/blog/automating-barcode-scanner-tests-with-capybara&quot;&gt;See it on Giant Robots&lt;/a&gt;&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>diff-check is on GitHub Marketplace</title>
        <link href="https://nickcharlton.net/posts/diff-check-is-on-github-marketplace.html" />
        <id>https://nickcharlton.net/posts/diff-check-is-on-github-marketplace.html</id>
        <published>Fri, 27 Sep 2024 00:00:00 +0000</published>
        <updated>Fri, 27 Sep 2024 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Today, I cut the &lt;a href=&quot;https://github.com/nickcharlton/diff-check/releases&quot;&gt;first release (v1.0.0)&lt;/a&gt; and &lt;a href=&quot;https://github.com/marketplace/actions/diff-check&quot;&gt;published diff-check to
GitHub Marketplace&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/nickcharlton/diff-check&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;diff-check&lt;/code&gt;&lt;/a&gt; came out of seeing a common pattern of seeing side effects
when another automated process would run, creating more work which wouldn’t be
seen at the time. It was really common when I was working on React Native
projects (if you upgrade an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm&lt;/code&gt; dependency, you’ll often get a change in the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Podfile.lock&lt;/code&gt; that Dependabot wouldn’t know about), but less frequently in
projects using Appraisal (another project I maintain). I &lt;a href=&quot;https://nickcharlton.net/posts/diff-check-github-action&quot;&gt;previously wrote up
how it works in the announcement blog post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With some help from &lt;a href=&quot;https://github.com/oscargus&quot;&gt;Oscar Gustafsson&lt;/a&gt; (thank you!), he helped see some bits
I’d got wrong and also pointed out some small ways in which we could improve
the output too. Several weeks ago, &lt;a href=&quot;https://github.com/thoughtbot/administrate/pull/2609&quot;&gt;I’d also merged in a PR to use this on
Administrate&lt;/a&gt; and so now I’m pretty happy it’s working.&lt;/p&gt;

&lt;p&gt;I’d love to hear if it works out for you!&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Generating Rails projects from Git patches</title>
        <link href="https://nickcharlton.net/posts/rails-projects-from-git-patches.html" />
        <id>https://nickcharlton.net/posts/rails-projects-from-git-patches.html</id>
        <published>Wed, 11 Sep 2024 00:00:00 +0000</published>
        <updated>Wed, 11 Sep 2024 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;A couple of weeks ago, &lt;a href=&quot;https://ruby.social/@jayroh/112893332809536544&quot;&gt;Joel asked&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Starting a new rails app is fun!&lt;/p&gt;

  &lt;p&gt;But after setting my bespoke set of preferred gems and config for the
N-hundred’th time I think it may be time for some sort of
template/generator/whatever.&lt;/p&gt;

  &lt;p&gt;Any tips?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, &lt;a href=&quot;https://mastodon.nickcharlton.net/@nick/112893484600600333&quot;&gt;I mentioned the approach I’ve used several times recently, using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt;’s
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;format-patch&lt;/code&gt; feature&lt;/a&gt;, but it really needs walking through to understand
how this is done.&lt;/p&gt;

&lt;p&gt;Patches are how you sent commits over email, something still done on a lot of
projects but gradually becoming more esoteric. But they’re very helpful. It’s a
plain-text file with the commit information (author, time, message) and the
change set from that commit. The key commands are &lt;a href=&quot;https://git-scm.com/docs/git-format-patch&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;format-patch&lt;/code&gt;&lt;/a&gt; to create
the patches, and &lt;a href=&quot;https://git-scm.com/docs/git-am&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;am&lt;/code&gt;&lt;/a&gt; which lets you apply them.&lt;/p&gt;

&lt;p&gt;To do this, we start with an existing, sort of “template” project that’s setup
how you’d like. But typically, I’ll fetch from the last one I took this
approach with. The key bit is having well isolated and phrased commits, like
this:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git show 020090b
commit 020090b47d75d3e77c9ee55518e5fcd9074d5c53
Author: Nick Charlton &amp;lt;nick@nickcharlton.net&amp;gt;
Date:   Mon Feb 26 14:31:59 2024 +0000

    Setup a Rails app &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;#1)&lt;/span&gt;

    rails new TemplateProject &lt;span class=&quot;nt&quot;&gt;--database&lt;/span&gt; postgresql &lt;span class=&quot;nt&quot;&gt;--skip-keeps&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;--skip-action-mailbox&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--skip-action-text&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--skip-active-storage&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;--skip-action-cable&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--skip-hotwire&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--skip-jbuilder&lt;/span&gt;

    Plus: RSpec, FactoryBot, lograge, rack-timeout, flashes and i18n &lt;span class=&quot;nb&quot;&gt;test
    &lt;/span&gt;configuration.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s the first few items of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; history for a project I put together at
the start the year to experiment with an idea, which served as the “template
project”:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git log &lt;span class=&quot;nt&quot;&gt;--oneline&lt;/span&gt;
1f26eed &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;HEAD -&amp;gt; main, origin/main, origin/HEAD&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Enable UUID primary keys &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;#6)&lt;/span&gt;
4d8dd1c Accept connections from an external tunnel &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;#5)&lt;/span&gt;
96f4241 Setup Ruby linting with standardrb &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;#4)&lt;/span&gt;
a3d0e2a Setup Bundle Audit &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;#3)&lt;/span&gt;
5bce041 Setup GitHub Actions &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;running tests &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;#2)&lt;/span&gt;
020090b Setup a Rails app &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;#1)&lt;/span&gt;
dc277a8 Initial commit&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; add README
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;These commits are were themselves from a another project I’d thrown together to
experiment with something completely different, and the one before &lt;em&gt;that&lt;/em&gt; was
when I sat down and figured out the bits I really cared about in Rails
projects. But they’re all from around the same era — it’s all Rails 7.1.&lt;/p&gt;

&lt;p&gt;We can generate patches up to the “initial commit” — the first commit in a Git
repository is special, so we want to avoid that — and then apply them to
another project:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git format-patch dc277a8
0001-Setup-a-Rails-app-1.patch
0002-Setup-GitHub-Actions-for-running-tests-2.patch
0003-Setup-Bundle-Audit-3.patch
0004-Setup-Ruby-linting-with-standardrb-4.patch
0005-Accept-connections-from-an-external-tunnel-5.patch
0006-Enable-UUID-primary-keys-6.patch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ll create a new branch, then apply the patch: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git am
0001-Setup-a-Rails-app-1.patch&lt;/code&gt;. As long as you apply them in the order they
were written, they’ll apply cleanly. If not, you can always modify the diff
inside the patch. Then, I’ll go ahead and modify that commit until it’s in the
state I want it to be:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Change any name references to the new project,&lt;/li&gt;
  &lt;li&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bin/setup&lt;/code&gt; and check it works,&lt;/li&gt;
  &lt;li&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bin/dev&lt;/code&gt; and check it works,&lt;/li&gt;
  &lt;li&gt;Update any gems that need updating, to save needing to do it immediately after,&lt;/li&gt;
  &lt;li&gt;Finally, before merging this branch, I’ll update the author/commit dates to
avoid confusing myself in future (e.g.: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git commit --amend --date=now&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this example, the other patches generated are around linting, GitHub Actions
for CI, Bundle Audit to catch vulnerable dependencies and some others like
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UUID&lt;/code&gt; primary keys. In the project I’m creating whilst writing this, I don’t
care about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UUID&lt;/code&gt; so I’ll just skip that but I’ll want the rest.&lt;/p&gt;

&lt;p&gt;I’ll caveat this approach, however, with a &lt;em&gt;you probably shouldn’t do it this
way&lt;/em&gt;. From maintaining &lt;a href=&quot;https://github.com/thoughtbot/administrate&quot;&gt;Administrate&lt;/a&gt; and having many ideas I’d like to try
out, I create a lot of throw-away Rails projects.&lt;/p&gt;

&lt;p&gt;For many years, I’d used &lt;a href=&quot;https://github.com/thoughtbot/suspenders&quot;&gt;Suspenders&lt;/a&gt; but it went through a period where
using it was pretty unreliable as we were struggling to keep up with Rails
changes. Now we’re at the end of 2024, we’ve fixed that, Suspenders is once
again in a good place and there’s been a big boon in &lt;a href=&quot;https://guides.rubyonrails.org/rails_application_templates.html&quot;&gt;Rails Templates&lt;/a&gt; too.
But now you know you can do it completely differently, if you fancied it.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Running PowerShell scripts locally with Packer</title>
        <link href="https://nickcharlton.net/posts/running-powershell-scripts-locally-with-packer.html" />
        <id>https://nickcharlton.net/posts/running-powershell-scripts-locally-with-packer.html</id>
        <published>Thu, 15 Aug 2024 00:00:00 +0000</published>
        <updated>Thu, 15 Aug 2024 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I maintain a set of virtual machine templates that use &lt;a href=&quot;https://www.packer.io&quot;&gt;Packer&lt;/a&gt;. For
&lt;a href=&quot;https://www.vmware.com&quot;&gt;VMware&lt;/a&gt; VMs, the workflow is to build them locally using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vmware-iso&lt;/code&gt;
builder, then push to vSphere using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vsphere&lt;/code&gt; post-processor. The idea
behind this approach is to be able to use the same templates for local VMs
(which are exported as an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ova&lt;/code&gt; archive) as those that end up on vSphere.
Unfortunately, there’s some differences between VMware Workstation and vSphere
that create some tricky problems like mismatched operating system versions.&lt;/p&gt;

&lt;p&gt;Fortunately, &lt;a href=&quot;https://docs.vmware.com/en/VMware-PowerCLI/index.html&quot;&gt;PowerCLI&lt;/a&gt; exists which makes it fairly pleasant to work with
vSphere from PowerShell. Packer can run local scripts, so I hatched the plan to
run a PowerShell script (using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shell-local&lt;/code&gt; provisioner) that would adjust
settings after the build had run. Alas, this is one of those problems that took
many months of evenings to figure out how to do.&lt;/p&gt;

&lt;p&gt;I’m using Packer v1.11.2 and PowerShell 7.4.4, and running everything on Linux.
Here’s an example that works, and an explanation of how it got here below:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-pwsh&quot;&gt;# hello-world.ps1
Write-Output &quot;Hello world!&quot;

Write-Output $env:MY_VAR
Write-Output $env:WITH_SPACES
Write-Output $env:PACKER_BUILDER_TYPE
Write-Output $env:PACKER_BUILD_NAME

Write-Output &quot;and done!&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-hcl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# example.pkr.hcl&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;null&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;example&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;communicator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;none&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;build&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;source.null.example&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;provisioner&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;shell-local&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;                &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;MY_VAR&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;hi&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;WITH_SPACES&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;and again&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;execute_command&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;pwsh&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;-Command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;amp; {   }&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;env_var_format&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;$env:%s=&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;; &quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;script&lt;/span&gt;             &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;hello-world.ps1&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;packer build example.pkr.hcl
null.example: output will be &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;this color.

&lt;span class=&quot;o&quot;&gt;==&amp;gt;&lt;/span&gt; null.example: Running &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;shell script: hello-world.ps1
    null.example: Hello world!
    null.example: hi
    null.example: hello world
    null.example: null
    null.example: example
    null.example: and &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;
Build &lt;span class=&quot;s1&quot;&gt;&apos;null.example&apos;&lt;/span&gt; finished after 450 milliseconds 889 microseconds.

&lt;span class=&quot;o&quot;&gt;==&amp;gt;&lt;/span&gt; Wait completed after 450 milliseconds 927 microseconds

&lt;span class=&quot;o&quot;&gt;==&amp;gt;&lt;/span&gt; Builds finished. The artifacts of successful builds are:
&lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; null.example: Did not &lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;anything. This is the null builder
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;This example uses the “null” builder, as we just want to run a local file,
it’s really helpful for testing,&lt;/li&gt;
  &lt;li&gt;Using &lt;a href=&quot;https://developer.hashicorp.com/packer/docs/post-processors/shell-local&quot;&gt;“shell-local”&lt;/a&gt; we can execute a file on the local filesystem, which
would usually default to a shell script (on a Unix),&lt;/li&gt;
  &lt;li&gt;But, this can be any command, and there’s a few options we can use to adjust
how the command is put together,&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.hashicorp.com/packer/docs/post-processors/shell-local#default-environmental-variables&quot;&gt;Packer exposes the builder type and build name as environment variables&lt;/a&gt;
or anything set in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;environment_vars&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;env&lt;/code&gt;, which will be key to
providing things like secrets to the script later on,&lt;/li&gt;
  &lt;li&gt;Unfortunately, &lt;a href=&quot;https://github.com/PowerShell/PowerShell/issues/3316&quot;&gt;PowerShell doesn’t accept environment variables as
arguments&lt;/a&gt;, and seems to fail when assigned ahead of the command (e.g.:
` bash ‘’` would usually work in an existing shell
session),&lt;/li&gt;
  &lt;li&gt;Instead we can use a PowerShell block (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{ }&lt;/code&gt;), and use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; in front to
execute immediately,&lt;/li&gt;
  &lt;li&gt;There’s two key things this is doing to make this work reliably, first, we
provide a different template to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;env_var_format&lt;/code&gt; so that the output is how
PowerShell expects it’s environment variables (and how it ends up in the
block), secondly, we use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;env&lt;/code&gt; rather than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;environment_vars&lt;/code&gt; and provide a
map of values as this means that the output is escaped correctly&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>thoughtbot Open Source Maintainers Sync notes is public</title>
        <link href="https://nickcharlton.net/posts/thoughtbot-open-source-maintainers-sync-public.html" />
        <id>https://nickcharlton.net/posts/thoughtbot-open-source-maintainers-sync-public.html</id>
        <published>Tue, 02 Jul 2024 00:00:00 +0000</published>
        <updated>Tue, 02 Jul 2024 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;We’ve been working recently on coming together as maintainers at work, trying
to share what we’ve been getting up to and seeing how we can help each other
out.&lt;/p&gt;

&lt;p&gt;Then we thought about sharing our notes in public, and now they’re available
for everyone.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Looking for an opportunity to contribute to Open Source? Look no more. We’ve
made our monthly internal Open Source Maintainers Sync notes public to
facilitate getting more people into contributing to Open Source.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://thoughtbot.com/blog/open-source-maintainers-sync-notes-is-public&quot;&gt;See it on Giant Robots&lt;/a&gt;&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Installing Opnsense with virt-install on KVM</title>
        <link href="https://nickcharlton.net/posts/installing-opnsense-virt-install-kvm-serial.html" />
        <id>https://nickcharlton.net/posts/installing-opnsense-virt-install-kvm-serial.html</id>
        <published>Fri, 07 Jun 2024 00:00:00 +0000</published>
        <updated>Fri, 07 Jun 2024 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I was trying to setup a quick (…foreshadowing) VM to run a VPN on a host I use 
for a few VMs with KVM. But getting it setup was surprisingly painful until I 
tried the “nano” install image.&lt;/p&gt;

&lt;p&gt;As the host is headless, I wanted a disk image I could import on the host with 
&lt;a href=&quot;https://opnsense.org&quot;&gt;Opnsense&lt;/a&gt; already installed to avoid trying to redirect VNC over the 
network, or something similarly daft. A serial terminal was ideal (and how 
&lt;a href=&quot;https://manpages.debian.org/stable/virtinst/virt-install.1.en.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;virt-install&lt;/code&gt;&lt;/a&gt; and the rest of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libvirt&lt;/code&gt; works nicest). Alas, I &lt;a href=&quot;https://forums.freebsd.org/threads/installing-freebsd-over-serial-console.62005/#post-357713&quot;&gt;couldn’t 
get a serial terminal configured correctly with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dvd&lt;/code&gt; image (it’d output 
boot logs, but when I got to the stage of interacting with the console it would 
stop outputting anything)&lt;/a&gt;, &lt;a href=&quot;https://docs.opnsense.org/manual/how-tos/serial_access.html&quot;&gt;the docs only mention using the Web UI&lt;/a&gt; 
which I couldn’t do at that stage of the install, and then the USB &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;serial&lt;/code&gt; 
image just wouldn’t boot.&lt;/p&gt;

&lt;p&gt;But the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nano&lt;/code&gt; image provided the eventual solution: it’s designed to be 
written to a disk and then just booted where it expands to fill the new volume 
and serial is already configured. Perfect, but it wasn’t clear how to do it. 
So:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Fetch and extract the &lt;a href=&quot;https://opnsense.org/download/&quot;&gt;image from the download page&lt;/a&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget https://mirror.ams1.nl.leaseweb.net/opnsense/releases/24.1/OPNsense-24.1-nano-amd64.img.bz2
bunzip2 OPNsense-24.1-nano-amd64.img.bz2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;Convert to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;qcow2&lt;/code&gt; disk image: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;qemu-img convert -f raw -O qcow2 OPNsense-24.1-nano-amd64.img OPNsense-24.1-nano-amd64.qcow2&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Resize to give us more space for logs, etc: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;qemu-img resize OPNsense-24.1-nano-amd64.qcow2 +8G&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Rename/put the disk somewhere helpful: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp OPNsense-24.1-nano-amd64.qcow2 opnsense.qcow2&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;virt-install&lt;/code&gt; to import into a new VM:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;virt-install --connect qemu:///system \
  --name opnsense \
  --os-variant freebsd12.2 \
  --memory 4096 \
  --disk opnsense.qcow2 \
  --network default \
  --graphics none \
  --console pty,target_type=serial \
  --import
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Hopefully this saves someone some future bother!&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>diff-check: A GitHub Action which fails if anything changed</title>
        <link href="https://nickcharlton.net/posts/diff-check-github-action.html" />
        <id>https://nickcharlton.net/posts/diff-check-github-action.html</id>
        <published>Sat, 17 Feb 2024 00:00:00 +0000</published>
        <updated>Sat, 17 Feb 2024 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Last year, I started to see a few common workflows that would create more work
by those unaware or open yourself up to making annoying mistakes if you did,
that I thought could be caught with just a little more automation.&lt;/p&gt;

&lt;p&gt;This was typically when &lt;a href=&quot;https://docs.github.com/en/code-security/dependabot/working-with-dependabot&quot;&gt;Dependabot&lt;/a&gt; would go and bump a dependency that
would have other implications: for example, if you bump a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm&lt;/code&gt; dependency in a
React Native project, it might change the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Podfile.lock&lt;/code&gt; in the iOS portion of
the codebase, or if you bump a dependency used by &lt;a href=&quot;https://github.com/thoughtbot/appraisal&quot;&gt;Appraisal&lt;/a&gt;, one of it’s
generated files could change. It gets pretty annoying for everyone else if you
merge in that seemingly okay Dependabot PR and then someone (even if it’s
yourself) has to go and add another commit to add in the forgotten lock file
change.&lt;/p&gt;

&lt;p&gt;To try and tackle this, I’ve put together a GitHub Action called “diff-check”.
It runs a command and fails if anything subsequently changes. For example, you
might create a workflow that looks like this:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;diff-check&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;demo&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nickcharlton/diff-check@main&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;echo &quot;hello world&quot; &amp;gt;&amp;gt; README.md&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It would then fail because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;README.md&lt;/code&gt; changed and report on the summary page:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/diff-check-demo.png&quot; alt=&quot;A screenshot of the Action &apos;diff-check&apos; reporting the results
           of how it run, displaying that the &apos;README&apos; file had changed.&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;The demo running and showing the job summary.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;At its heart, &lt;a href=&quot;https://github.com/nickcharlton/diff-check/blob/838eaa28f7bb1deb61479f75cf80bafcda0a7433/bin/diff-check&quot;&gt;it’s a fairly short shell script&lt;/a&gt;, that’s &lt;a href=&quot;https://github.com/nickcharlton/diff-check/blob/838eaa28f7bb1deb61479f75cf80bafcda0a7433/action.yml&quot;&gt;called by the action&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/sh&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; git diff-index &lt;span class=&quot;nt&quot;&gt;--quiet&lt;/span&gt; HEAD&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
	&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;printf&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;These files changed when running the command:\n\n&apos;&lt;/span&gt;

	git diff &lt;span class=&quot;nt&quot;&gt;--name-only&lt;/span&gt; | &lt;span class=&quot;k&quot;&gt;while &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; n &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
		&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;* &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;done

	&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But there’s enough logic in here that I really wanted some tests to make sure
it was correct. I turned to &lt;a href=&quot;https://github.com/odlp/jet_black&quot;&gt;Oli’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jet_black&lt;/code&gt;&lt;/a&gt;, which I’ve been meaning to
try out for years and let me write some tests using RSpec. &lt;a href=&quot;https://github.com/nickcharlton/diff-check/blob/838eaa28f7bb1deb61479f75cf80bafcda0a7433/spec/black_box/diff-check_spec.rb&quot;&gt;I’m pretty happy
with how that turned out&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Trying to solve problems like these in such an abstract way is far harder than
first solving a specific problem. I’d originally created the repo back in July
of last year, and I really wanted the implementation to be able to suggest what
needed to be done through a review suggestion, but this &lt;a href=&quot;https://github.com/orgs/community/discussions/9099&quot;&gt;isn’t possible despite
a long-standing feature request for it&lt;/a&gt;. In the end, this was the common
thread through the situations I kept seeing. Hopefully it might help out some
others too.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Fixing unsupported SFP+ modules/&quot;no carrier&quot; errors with Intel X520 cards on Debian &amp; FreeBSD</title>
        <link href="https://nickcharlton.net/posts/unsupported-sfp-modules-intel-x520-debian-freebsd.html" />
        <id>https://nickcharlton.net/posts/unsupported-sfp-modules-intel-x520-debian-freebsd.html</id>
        <published>Mon, 15 Jan 2024 00:00:00 +0000</published>
        <updated>Mon, 15 Jan 2024 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I’ve been reconfiguring a router I built at the end of last year to be a
&lt;a href=&quot;https://docs.opnsense.org/manual/how-tos/transparent_bridge.html&quot;&gt;transparent firewall (or filtering bridge) using Opnsense&lt;/a&gt;. On the router,
I’ve got a Dell-branded Intel X520-DA2 NIC, along with some &lt;a href=&quot;https://www.flypro.com/p423.html&quot;&gt;Flypro 10GBase-T
SFP+ modules&lt;/a&gt; which I picked up on offer last year. But … they don’t work.&lt;/p&gt;

&lt;p&gt;Intel — like a lot of other vendors — restricts the SFP+ modules
to those which have been approved, regardless of whether they may pose a
problem and so you can’t just use any SFP+ module which isn’t programmed for
that vendor’s hardware. I’d not actually considered that this was going to be a
problem (even knowing about the vendor restriction with some cards), so it
derailed the project whilst I tried to debug what was going wrong, as at least
in Opnsense this wasn’t that clear.&lt;/p&gt;

&lt;p&gt;You might see disconnected interfaces which list “no carrier” even when modules
are inserted, or just no interfaces at all (if you booted with them in, the
driver won’t use them). The screenshot below shows when the interfaces &lt;em&gt;do&lt;/em&gt;
show up, but this required booting up with no SFP+ modules, then inserting them
afterwards.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/unsupported-sfp-intel-x520.png&quot; alt=&quot;Screenshot from Opnsense&apos;s interface section, showing no 
           carrier (even though there is).&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Screenshot from Opnsense&apos;s interface section, showing no carrier (even though
  there is).&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;After reconfiguring a few times, I noticed the interface was always
disconnected, regardless of what was physically plugged in or what I’d set up.
I’m not so familiar with Opnsense/FreeBSD, so booted up a Debian live image to
try out something I was familiar with. I’d originally thought that maybe it was
a platform driver issue, but booting Debian left me with the following error:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ixgbe 0000:04:00.0: failed to load because an unsupported SFP+ or QSFP module type was detected.
ixgbe 0000:04:00.0: Reload the driver after installing a supported module.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(You can find it later in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dmesg&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.fs.com/uk/c/fs-box-3389&quot;&gt;Some third-party vendors supply tools to re-programme the boards&lt;/a&gt;, but
these Flypro modules don’t support that (nor do I have one), but there are
options.&lt;/p&gt;

&lt;h2 id=&quot;debian-workaround&quot;&gt;Debian workaround&lt;/h2&gt;

&lt;p&gt;On Debian, &lt;a href=&quot;https://blog.route1.ph/2019/09/27/stretch-ixgbe-driver-allow-unsupported-sfp-modules-on-intel-x520-cards/&quot;&gt;we can disable the check in the driver module&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;root@debian:~# rmmod ixgbe
root@debian:~# modprobe ixgbe allow_unsupported_sfp=1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then bring the interfaces up (with a module in/remove and reinsert if already
there):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ip link set dev ens2f0 up
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we wanted to persist, it we could do:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;root@debian:~# echo &quot;options ixgbe allow_unsupported_sfp=1&quot; &amp;gt; /etc/modprobe.d/ixgbe.conf
root@debian:~# depmod -a
root@debian:~# update-initramfs -u -k `uname -r` # if you&apos;re not running a live system
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can then reload the module to bring up the new configuration:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;root@debian:~# rmmod ixgbe
root@debian:~# modprobe ixgbe
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;freebsd-workaround&quot;&gt;FreeBSD workaround&lt;/h2&gt;

&lt;p&gt;On &lt;a href=&quot;https://forums.freebsd.org/threads/status-no-carrier-for-ethernet-port.67721/&quot;&gt;FreeBSD (and so Opnsense, pfSense, etc), we see a similar log message&lt;/a&gt;
and &lt;a href=&quot;https://forums.freebsd.org/threads/intel-sfp-card-not-compatible.85348/&quot;&gt;can work around it similarly&lt;/a&gt;. On boot, we’ll see (and also in
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/log/dmesg.today&lt;/code&gt;):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ix2: Unsupported SFP+ module detected!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can then &lt;a href=&quot;https://wiki.freebsd.org/sysctl&quot;&gt;set a tunable&lt;/a&gt; to allow unsupported modules:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;echo &quot;hw.ix.unsupported_sfp=1&quot; &amp;gt; /boot/loader.conf.d/ix.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After a reboot, the interface should be usable. I did this on Opnsense and it
persists between reboots, and also when resetting to factory defaults.&lt;/p&gt;

&lt;h2 id=&quot;disabling-the-check-on-the-card&quot;&gt;Disabling the check on the card&lt;/h2&gt;

&lt;p&gt;These workarounds were really helpful to isolate my problem to the restriction
and not any other hardware or software configuration, but it’d be much better
to just not have to think about this problem again, especially as this
workaround is reportedly not possible with something like Windows or ESXi, both
of which I use elsewhere.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://forums.servethehome.com/index.php?threads/patching-intel-x520-eeprom-to-unlock-all-sfp-transceivers.24634/&quot;&gt;Over on the ServeTheHome forums, &lt;em&gt;NathanA&lt;/em&gt;&lt;/a&gt; figured out exactly how to do
this after digging into various data sheets and support threads. &lt;a href=&quot;https://rymnd.net/blog/2020/unsupported-sfp-intel-x520/&quot;&gt;Raymond
Douglas then distilled this down into the steps&lt;/a&gt; required. It relies on
using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ethtool&lt;/code&gt; (I used a &lt;a href=&quot;https://cdimage.debian.org/debian-cd/current-live/amd64/iso-hybrid/&quot;&gt;“standard” (without desktop environment) live CD
image&lt;/a&gt;) to compare a bit (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IXGBE_DEVICE_CAPS_ALLOW_ANY_SFP&lt;/code&gt;, in the Linux
ixgbe driver) on the device’s EEPROM before writing a new version.&lt;/p&gt;

&lt;h3 id=&quot;1-identify-the-current-value&quot;&gt;1. Identify the current value&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;root@debian:~# ethtool -e enp4s0f0 offset 0x58 length 1
Offset             Values
------             -------
0x0058:            fc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;2-write-the-new-value&quot;&gt;2. Write the new value&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;root@debian:~# ethtool -E enp4s0f0 magic 0x10fb8086 offset 0x58 value 0xfd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;3-verify-by-reading-back&quot;&gt;3. Verify by reading back&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;root@debian:~# ethtool -e enp4s0f0 offset 0x58 length 1
Offset             Values
------             -------
0x0058:            fd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After a reboot, the Debian live environment then used the new module without
any further configuration.&lt;/p&gt;

&lt;p&gt;I’d really recommend reading the original ServeTheHome post, as it explains in
much more depth what’s going on, why this works and why you need to be careful
not to modify or set incorrect values, as you risk bricking the card.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Always showing all GitHub Checks with a user style sheet</title>
        <link href="https://nickcharlton.net/posts/github-checks-stylesheet.html" />
        <id>https://nickcharlton.net/posts/github-checks-stylesheet.html</id>
        <published>Thu, 11 Jan 2024 00:00:00 +0000</published>
        <updated>Thu, 11 Jan 2024 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;If you’ve worked with pull requests on GitHub for a while — especially
with Actions — you’re probably used to having a long list of checks,
that you find yourself scrolling every time to find the failing one:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/github-checks-scrolling-box.gif&quot; alt=&quot;An animated gif showing a long list of checks on a GitHub Pull
           Request, where the user is scrolling up and down to find the one
           which is failing&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;The usual view of GitHub checks when you&apos;ve got a lot, and one failing&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Last year, &lt;a href=&quot;https://eskola.uk&quot;&gt;Ben&lt;/a&gt; and I started working on a user style sheet to stop you
needing to scroll and instead show the whole section:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/github-checks-expanded-box.png&quot; alt=&quot;A screenshot showing the state after the user style sheet is
           used, with the scrolling area expanded to fit the amount of checks.&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;After the user style sheet, with the scrolling area expanded to fit the
  amount of checks.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I use &lt;a href=&quot;https://github.com/quoid/userscripts&quot;&gt;UserScripts&lt;/a&gt; in Safari, but I’ve heard good things of &lt;a href=&quot;https://add0n.com/stylus.html&quot;&gt;Stylus&lt;/a&gt;
too. We came up with this:&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;/* ==UserStyle==
@name        Expand GitHub PR Statuses
@description Always fully expand the statuses on the GitHub PR page
@match       https://github.com/*
==/UserStyle== */&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;/* don&apos;t restrict the height of the open state */&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.branch-action-item.open&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.merge-status-list-wrapper&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.merge-status-list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.branch-action-item.open&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.merge-status-list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;max-height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;/* always expand to fill the content of the above */&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.merge-status-list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;max-height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;/* collapse the hidden state */&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.merge-status-list.hide-closed-list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;max-height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ve used this since November and it works great!&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Resolving ESXi 7.0 NIC connection issues on Supermicro X10SDV-4C-TLN2F motherboards</title>
        <link href="https://nickcharlton.net/posts/resolving-esxi-7-nic-connection-issues-on-supermicro-x10sdv-4c-tln2f-motherboards.html" />
        <id>https://nickcharlton.net/posts/resolving-esxi-7-nic-connection-issues-on-supermicro-x10sdv-4c-tln2f-motherboards.html</id>
        <published>Fri, 17 Mar 2023 00:00:00 +0000</published>
        <updated>Fri, 17 Mar 2023 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I have a few &lt;a href=&quot;https://www.supermicro.com/en/products/motherboard/X10SDV-4C-TLN2F&quot;&gt;Supermicro X10SDV-4C-TLN2F motherboards&lt;/a&gt; that I’ve been
setting up to run &lt;a href=&quot;https://www.vmware.com/products/esxi-and-esx.html&quot;&gt;VMware ESXi&lt;/a&gt; on. The first board was fine, but when
setting up another I could never get ESXi to acquire an IP over DHCP. The
hardware was happy the connection was valid, and both Debian 11 and Windows
Server 2019 were fine …but not ESXi:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/supermicro-nic-waiting-for-dhcp.png&quot; alt=&quot;A screenshot showing ESXi with &apos;waiting for DHCP&apos;&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;A screenshot showing ESXi with &apos;waiting for DHCP&apos;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/supermicro-nic-adapters-disconnected.png&quot; alt=&quot;A screenshot showing the ESXi network adaptors dialog, with
           adaptor state as disconnected.&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;A screenshot showing the adaptors as disconnected.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I’d been using a slightly older version of ESXi (7.0U3D/19482537), so I tried a
newer version (7.0U3g/20328353) that I’ve upgraded to on one of the other
boards, but this didn’t help. Both of my boards were running the same BIOS
version (2.1) and build date (2019-11-22), so that seemed an unlikely cause.&lt;/p&gt;

&lt;p&gt;After more head-scratching, I came across &lt;em&gt;&lt;a href=&quot;https://tinkertry.com/how-to-work-around-intermittent-intel-x557-network-outages-on-12-core-xeon-d#may-06-2018-update&quot;&gt;Workaround and fix for intermittent
Intel X552/X557 10GbE/1GbE network link-down outages on Xeon D-1500
Series&lt;/a&gt;&lt;/em&gt;. I didn’t have the exact boards they were talking about in the
start of the post nor the intermittent issues mentioned, but the one I have
&lt;em&gt;does&lt;/em&gt; have an Intel X552 so I thought this was promising. After going through
the whole post (and most of the more recent comments), it seemed like it might
be a NIC firmware difference between the two of my boards. I’m using a basic
gigabit switch too, and not a 10GbE one.&lt;/p&gt;

&lt;h2 id=&quot;investigating-the-current-nic--firmware&quot;&gt;Investigating the current NIC &amp;amp; firmware&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://tinkertry.com/how-to-check-which-network-driver-your-esxi-server-is-currently-using&quot;&gt;Using the console, we can find out more about the hardware.&lt;/a&gt; When
connected directly (or using IPMI), &lt;a href=&quot;https://kb.vmware.com/s/article/2148363&quot;&gt;Alt+F1 will switch to the console&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;esxcli network nic list

Name    PCI Device    Driver   Admin Status  Link Status  Speed  Duplex  MAC Address         MTU  Description
&lt;span class=&quot;nt&quot;&gt;------&lt;/span&gt;  &lt;span class=&quot;nt&quot;&gt;------------&lt;/span&gt;  &lt;span class=&quot;nt&quot;&gt;-------&lt;/span&gt;  &lt;span class=&quot;nt&quot;&gt;------------&lt;/span&gt;  &lt;span class=&quot;nt&quot;&gt;-----------&lt;/span&gt;  &lt;span class=&quot;nt&quot;&gt;-----&lt;/span&gt;  &lt;span class=&quot;nt&quot;&gt;------&lt;/span&gt;  &lt;span class=&quot;nt&quot;&gt;-----------------&lt;/span&gt;  &lt;span class=&quot;nt&quot;&gt;----&lt;/span&gt;  &lt;span class=&quot;nt&quot;&gt;-----------&lt;/span&gt;
vmnic0  0000:03:00.0  ixgben   Up            Down             0  Half    ac:1f:6b:12:f8:6e  1500  Intel&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;R&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Ethernet Connection X552/X557-AT 10GBASE-T
vmnic1  0000:03:00.1  ixgben   Up            Down             0  Half    ac:1f:6b:12:f8:6f  1500  Intel&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;R&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Ethernet Connection X552/X557-AT 10GBASE-T

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;esxcli network nic get &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; vmnic0
  Advertised Auto Negotiation: &lt;span class=&quot;nb&quot;&gt;true
  &lt;/span&gt;Advertised Link Modes: Auto, 1000BaseT/Full, 10000BaseT/Full
  Auto Negotiation: &lt;span class=&quot;nb&quot;&gt;false
  &lt;/span&gt;Cable Type: Twisted Pair
  Current Message Level: &lt;span class=&quot;nt&quot;&gt;-1&lt;/span&gt;
  Driver Info:
      Bus Info: 0000:03:00:0
      Driver: ixgben
      Firmware Version: 0x800005ad
      Version: 1.7.1.35
  Link Detected: &lt;span class=&quot;nb&quot;&gt;true
  &lt;/span&gt;Link Status: Up
  Name: vmnice0
  PHYAddress: 0
  Pause Autonegotiate: &lt;span class=&quot;nb&quot;&gt;true
  &lt;/span&gt;Pause RX: &lt;span class=&quot;nb&quot;&gt;true
  &lt;/span&gt;Pause TX: &lt;span class=&quot;nb&quot;&gt;true
  &lt;/span&gt;Supported Ports: TP
  Supports Auto Megotiation: &lt;span class=&quot;nb&quot;&gt;true
  &lt;/span&gt;Supports Pause: &lt;span class=&quot;nb&quot;&gt;true
  &lt;/span&gt;Supports Hakeon: &lt;span class=&quot;nb&quot;&gt;false
  &lt;/span&gt;Transceiver:
  Virtual Address: 00:50:56:56:62:7
  Hakeon: None
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On the board with issues, the firmware version is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x800005ad&lt;/code&gt;. But the working
board is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x800003e7&lt;/code&gt;, which is curious.&lt;/p&gt;

&lt;h2 id=&quot;a-temporary-solution&quot;&gt;A Temporary Solution&lt;/h2&gt;

&lt;p&gt;I then tried the &lt;a href=&quot;https://tinkertry.com/how-to-install-esxi-on-xeon-d-1500-supermicro-superserver#comment-3869415400&quot;&gt;suggestion from Bruno Zeidan&lt;/a&gt;, who had the same board:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;esxcli network nic &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--speed&lt;/span&gt; 1000 &lt;span class=&quot;nt&quot;&gt;--duplex&lt;/span&gt; full &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; vmnic0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This worked — and continues to do so after a reboot. Unfortunately, if the
network cable is unplugged it won’t come back up again.&lt;/p&gt;

&lt;h2 id=&quot;upgrading-the-bios&quot;&gt;Upgrading the BIOS&lt;/h2&gt;

&lt;p&gt;My next thought was to try &lt;a href=&quot;https://www.supermicro.com/en/support/resources/downloadcenter/firmware/MBD-X10SDV-4C-TLN2F/BIOS&quot;&gt;the new BIOS release&lt;/a&gt;. Supermicro put out 2.3 on
2021-06-04 and upgrading the BIOS is enough of a faff I didn’t bother
previously.&lt;/p&gt;

&lt;p&gt;I wrote a &lt;a href=&quot;https://freedos.org/download/&quot;&gt;FreeDOS 1.3 LiveCD&lt;/a&gt; using &lt;a href=&quot;https://rufus.ie/en/&quot;&gt;Rufus&lt;/a&gt; to a USB drive and dropped
in the BIOS Zip contents. I’d originally tried to use the FreeDOS bundled with
Rufus, but this gave me an out of memory error (&lt;a href=&quot;https://www.bytesizedalex.com/supermicro-server-bios-update/&quot;&gt;like mentioned in this blog
post&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;After booting back into ESXi after flashing completed, it did immediately get
an IP, which was encouraging. Post upgrade, the firmware version was
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x800005ad&lt;/code&gt; still and unplugging the cable ended up with the same situation as
before.&lt;/p&gt;

&lt;h2 id=&quot;trying-to-flash-the-nic&quot;&gt;Trying to flash the NIC&lt;/h2&gt;

&lt;p&gt;Knowing that a difference between NIC firmware versions was the most likely
explanation for the different behaviour on each board, I was curious to see
what would happen if I tried to flash them. I also wasn’t convinced about the
hex values of the version number — Intel didn’t have those listed anywhere.&lt;/p&gt;

&lt;p&gt;I got a copy of the &lt;a href=&quot;https://www.supermicro.com/wdl/CDR_Images/CDR-X10-UP/&quot;&gt;most recent driver CD&lt;/a&gt; and moved the Intel NIC
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BOOTUTIL&lt;/code&gt; over to the FreeDOS USB drive from earlier. &lt;a href=&quot;https://calvin.me/how-to-update-intel-nic-firmware/&quot;&gt;Calvin Bui’s post about
flashing the NIC firmware&lt;/a&gt; enabled me to figure out what was needed. From
reading around, &lt;a href=&quot;https://community.intel.com/t5/Ethernet-Products/Intel-Ethernet-Connection-X552-x557-AT-Can-t-drive/m-p/1302105&quot;&gt;it seems like Supermicro likely have their own firmware
specifically for the onboard NIC&lt;/a&gt;, so I stuck to the &lt;a href=&quot;https://www.supermicro.com/wdl/CDR_Images/CDR-X10-UP/&quot;&gt;available (two)
driver CDs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On the problematic board, it turned out that the version was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.3.58&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C:\BOOTUTIL\DOS\bootutil.exe

Intel(R) Ethernet Flash Firmware Utility
BootUtil version 1.6.40.1
Copyright (C) 2003-2017 Intel Corporation

Type BootUtil -? for help

Port Network Address Location Series  WOL Flash Firmware     Version
==== =============== ======== ======= === ================== =======
  1   AC1F6B12F86E     3:00.0 10GbE   YES UEFI,PXE Enabled   2.3.58
  1   AC1F6B12F86F     3:00.0 10GbE   YES UEFI,PXE Enabled   2.3.58
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But the working board was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.3.53&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C:\BOOTUTIL\DOS\bootutil.exe

Intel(R) Ethernet Flash Firmware Utility
BootUtil version 1.6.40.1
Copyright (C) 2003-2017 Intel Corporation

Type BootUtil -? for help

Port Network Address Location Series  WOL Flash Firmware     Version
==== =============== ======== ======= === ================== =======
  1   OCC47A95BE18     3:00.0 10GbE   YES UEFI,PXE Enabled   2.3.53
  1   OCC47A95BE19     3:00.0 10GbE   YES UEFI,PXE Enabled   2.3.53
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NVMUpdate&lt;/code&gt; is the tool to flash the NIC firmware and the easiest way to run
this was to create a &lt;a href=&quot;https://www.debian.org/CD/live/&quot;&gt;Debian Live&lt;/a&gt; USB drive and copy over the versions
from the driver ISO images.&lt;/p&gt;

&lt;p&gt;Once you’re booted into Debian, you can:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Find the drive: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo fdisk -l&lt;/code&gt;,&lt;/li&gt;
  &lt;li&gt;Create a mount point: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo mkdir /media/usb-drive&lt;/code&gt;,&lt;/li&gt;
  &lt;li&gt;Then mount it, e.g.: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo mount /dev/sdb1 /media/usb-drive&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./nvmupdate64e&lt;/code&gt; would always give a device access error on all
available Supermicro versions (and the &lt;a href=&quot;https://www.intel.com/content/www/us/en/download/15084/intel-ethernet-adapter-complete-driver-pack.html?wapkw=x557-at&quot;&gt;most recent Intel version available
from their site&lt;/a&gt;):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo ./nvmupdate64e

Intel(R) Ethernet NVM Update Tool
NVMUpdate version 1.38.3.3
Copyright(C) 2013 - 2023 Intel Corporation.

WARNING: To avoid damage to your device, do not stop the update or reboot or
poweroff the system during this update.
Inventory in process. Please wait [...|******]

Num Description                          Ver.(hex)  DevId S:B    Status
=== ================================== ============ ===== ====== ==============
01) Intel(R) Ethernet Connection          N/A(N/A)   15AD 00:003 Access error
    X552/X557-AT 10GBASE-T

Tool execution completed with the following status: Device not found.
Press any key to exit.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now I was stumped. The next best option is to get in touch with Supermicro
support, but I’d love to hear from anyone who gets further than me!&lt;/p&gt;

&lt;p&gt;For now, I’m using the temporary fix mentioned above as I need to get on and
get it setup.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Building a 1Password CLI Windows MSI Installer</title>
        <link href="https://nickcharlton.net/posts/building-a-1password-cli-windows-msi-installer.html" />
        <id>https://nickcharlton.net/posts/building-a-1password-cli-windows-msi-installer.html</id>
        <published>Fri, 17 Mar 2023 00:00:00 +0000</published>
        <updated>Fri, 17 Mar 2023 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;https://developer.1password.com/docs/cli/&quot;&gt;1Password has a CLI tool&lt;/a&gt; that’s very helpful for integrating into things
like setup scripts and handling development secrets more securely.&lt;/p&gt;

&lt;p&gt;Unfortunately, on Windows, there’s no installer. Instead, &lt;a href=&quot;https://developer.1password.com/docs/cli/get-started#install&quot;&gt;you’re prompted to
extract an archive, create the destination directory and add it to your
PATH&lt;/a&gt;. This is annoying once, impractical to do multiple times and enough of
a hurdle for others to be quite a pain, especially when you want to keep up to
date with the most recent version.&lt;/p&gt;

&lt;p&gt;I’m sure 1Password will release one eventually, but for now, I’ve put together
a quick &lt;a href=&quot;https://github.com/nickcharlton/1password-cli-msi-installer&quot;&gt;project to make installers that you can find on GitHub&lt;/a&gt;. &lt;a href=&quot;https://github.com/nickcharlton/1password-cli-msi-installer/releases&quot;&gt;On the
releases page&lt;/a&gt;, you can find built versions for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i386&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;amd64&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I’ll keep these updated until 1Password releases their own, but please let me
know if you see any problems (and if you find them useful too)! I would like to
have these code signed eventually, but for now, that’s not something I can do.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This project initially came out of a frantic evening back in December 2022
where I got caught up in trying to assemble a basic installer with &lt;a href=&quot;https://wixtoolset.org&quot;&gt;WiX
Toolset&lt;/a&gt;, initially inspired by trying to resolve an installation issue
where we’ve been &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/uwp/enterprise/#msix-deployment&quot;&gt;side-loading an enterprise UWP app&lt;/a&gt;. Whilst there’s much
more to do (and another blog post to go with that topic), assembling the
missing installer for the 1Password CLI seemed like an excellent first step.&lt;/p&gt;

&lt;p&gt;I elected to use &lt;a href=&quot;https://wixtoolset.org/docs/fourthree/&quot;&gt;WiX v3, instead of v4 as v3&lt;/a&gt; has much better information
about it. Using v3 meant I wasn’t also trying to understand how to build
installers at the same time as trying to understand a whole new version of a
tool along with the rest of the community, and there’s a lot of “TODO: WiX v4
documentation is under development.” in the docs, which wasn’t too inspiring.&lt;/p&gt;

&lt;p&gt;As installer projects go, it’s pretty straightforward. There’s two build
architectures defined, along with a set of platform-dependent variables
(in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Config.wxi&lt;/code&gt;):&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;Include&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;cp&quot;&gt;&amp;lt;?if $(var.Platform) = x64?&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;cp&quot;&gt;&amp;lt;?define PlatformProductName = &quot;1Password CLI (x64)&quot;?&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;cp&quot;&gt;&amp;lt;?define PlatformUpgradeCode = &quot;D7D4A655-76AB-45C9-B50F-0A8C1009E8F5&quot;?&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;cp&quot;&gt;&amp;lt;?define PlatformProgramFilesFolder = &quot;ProgramFiles64Folder&quot;?&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;cp&quot;&gt;&amp;lt;?else ?&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;cp&quot;&gt;&amp;lt;?define PlatformProductName = &quot;1Password CLI (x86)&quot;?&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;cp&quot;&gt;&amp;lt;?define PlatformUpgradeCode = &quot;E21CFAE2-49F0-489C-A069-437374D61DA5&quot;?&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;cp&quot;&gt;&amp;lt;?define PlatformProgramFilesFolder = &quot;ProgramFilesFolder&quot;?&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;cp&quot;&gt;&amp;lt;?endif ?&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Include&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the main &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Product.wxs&lt;/code&gt; file, first, the target directory is setup:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Directory&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;TARGETDIR&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SourceDir&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Directory&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;$(var.PlatformProgramFilesFolder)&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Directory&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;INSTALLFOLDER&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1Password CLI&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Directory&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Directory&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ComponentGroup&lt;/code&gt; is defined that places the executable (kept at the root
of the project) and sets up the environment variable:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ComponentGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Directory=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;INSTALLFOLDER&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ProductComponentGroup&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Component&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cmp_op.exe&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Guid=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;cp&quot;&gt;&amp;lt;?if $(var.Platform) = x64 ?&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;File&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;KeyPath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;yes&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;op.exe&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Source=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;op_x64.exe&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;cp&quot;&gt;&amp;lt;?else ?&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;File&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;KeyPath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;yes&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;op.exe&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Source=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;op_x86.exe&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;cp&quot;&gt;&amp;lt;?endif ?&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Component&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Component&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cmp_EnvironmentVariable&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Guid=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;46D0B3FB-60B3-4F08-A911-CC03F3907DC2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;KeyPath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;yes&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Environment&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;InstallPath&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Path&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[INSTALLFOLDER]&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;Action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;set&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;System=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;yes&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;Part=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;last&quot;&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;Separator=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Component&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Component&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cmp_RegistryEntry&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Guid=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;RegistryKey&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Root=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;HKLM&apos;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Software\[Manufacturer]\[ProductName]&apos;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;RegistryValue&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;KeyPath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;yes&apos;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;string&apos;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Install location&apos;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;[INSTALLFOLDER]&apos;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/RegistryKey&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Component&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ComponentGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Compiling independently for each architecture builds an installer in
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1PasswordCliInstaller\bin\Release&lt;/code&gt; with this setup.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/kurtanr&quot;&gt;kurtanr&lt;/a&gt;’s &lt;a href=&quot;https://github.com/kurtanr/WiXInstallerExamples&quot;&gt;WiXInstallerExamples&lt;/a&gt; project was very helpful in trying to
put this together, and many more examples if you find yourself trying to do
something similar.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Setting up an Azure Site-to-Site VPN to a Ubiquiti EdgeRouter through NAT</title>
        <link href="https://nickcharlton.net/posts/azure-site-to-site-vpn-ubiquiti-edgerouter-nat.html" />
        <id>https://nickcharlton.net/posts/azure-site-to-site-vpn-ubiquiti-edgerouter-nat.html</id>
        <published>Tue, 03 Jan 2023 00:00:00 +0000</published>
        <updated>Tue, 03 Jan 2023 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I have a Home Lab with a couple of devices for experiments and testing. It’s
set up on a separate physical network — using a &lt;a href=&quot;https://store.ui.com/collections/operator-edgemax-routers/products/edgerouter-x&quot;&gt;Ubiquiti EdgeRouter X&lt;/a&gt; —
but I wanted to connect this to the outside world and get access to public IPs
I could proxy to local services.&lt;/p&gt;

&lt;p&gt;A while ago, I started using &lt;a href=&quot;https://azure.microsoft.com&quot;&gt;Azure&lt;/a&gt; for other experiments (because I have a
bunch of credits to use). I’ve also found the cost of the more basic virtual
machines cheaper than other cloud providers. At the time of writing, &lt;a href=&quot;https://azure.microsoft.com/en-gb/pricing/details/virtual-machines/linux/&quot;&gt;a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;B1s&lt;/code&gt;
is £3.19/m&lt;/a&gt; on pay-as-you-go and down to £1.20/m reserved for three years)
and importantly, allowed me to allocate the most IPs to a single virtual
machine.&lt;/p&gt;

&lt;p&gt;On the Azure side, I’m using a &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/vpn-gateway/&quot;&gt;VPN Gateway&lt;/a&gt; associated with a &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/virtual-network/virtual-networks-overview&quot;&gt;virtual
network&lt;/a&gt;. On the Home Lab side, a Ubiquiti EdgeRouter X behind a &lt;a href=&quot;https://store.ui.com/products/unifi-security-gateway&quot;&gt;Ubiquiti
Security Gateway&lt;/a&gt; (which is doing NAT for my home network). I don’t have a
static IP, but it changes infrequently, so this shouldn’t be too much of a
problem.&lt;/p&gt;

&lt;p&gt;To configure a Site-to-Site VPN, both sides of the connection need to know
about each other. We can’t do this with NAT — as NAT hides the devices behind
it — but we can work around this by forwarding the necessary ports for IPSec on
the local side. In some ways, this is a bit of a cop-out solution, but
otherwise, we’d need a Point-to-Site that isn’t supported on the lower-end VPN
Gateway.&lt;/p&gt;

&lt;p&gt;(I initially started this project by trying to build a custom &lt;a href=&quot;https://vyos.io&quot;&gt;VyOS&lt;/a&gt; image
and do this using a VyOS VM on the Azure side and the EdgeRouter locally. After
months of on-and-off effort, I got the VM working, but it’s challenging to
configure when both sides are behind NAT. I also realised that the lowest-tier
&lt;a href=&quot;https://azure.microsoft.com/en-gb/pricing/details/vpn-gateway/&quot;&gt;VPN Gateway worked out to be about £22/m&lt;/a&gt;, which is fine.)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://help.ui.com/hc/en-us/articles/115012221027-EdgeRouter-Policy-Based-Site-to-Site-IPsec-VPN-to-Azure-IKEv1-IPsec-&quot;&gt;For the rest of this article, I’ve based it on this Ubiquiti help guide, but
using Terraform for the configuration&lt;/a&gt;. We’ll configure an IKEv1/IPSec VPN
using a shared secret and end up being to connect our local network to virtual
machines running in our Azure virtual network.&lt;/p&gt;

&lt;h2 id=&quot;local-network-adjustments&quot;&gt;Local Network Adjustments&lt;/h2&gt;

&lt;p&gt;IPSec uses UDP ports 4500 and 500 over UDP. I configured that in the UniFi
controller to point to the EdgeRouter.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/site-to-site-vpn-unifi-port-forward.png&quot; alt=&quot;A screenshot showing the UniFi controller, forwarding IPSec ports&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;A screenshot showing the UniFi controller, forwarding IPSec ports&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;configuring-the-azure-virtual-network-gateway-using-terraform&quot;&gt;Configuring the Azure Virtual Network Gateway using Terraform&lt;/h2&gt;

&lt;p&gt;We need to configure a few things to get this up and running (and this assumes
you already have a resource group (named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rg&lt;/code&gt; here):&lt;/p&gt;

&lt;div class=&quot;language-terraform highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_local_network_gateway&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;home_lab&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;                &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;HomeLab&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resource_group_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;gateway_address&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;LOCAL_NETWORK_PUBLIC_IP&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;address_space&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;10.1.0.0/24&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# local network subnet&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_subnet&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;gateway&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;                 &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;GatewaySubnet&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resource_group_name&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;virtual_network_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_virtual_network&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;core&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;address_prefixes&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;10.0.1.0/24&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_public_ip&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;gateway&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;                &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Gateway&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resource_group_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;allocation_method&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Dynamic&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_virtual_network_gateway&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;gateway&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;                &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Gateway&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resource_group_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt;                &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Vpn&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;vpn_type&lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;PolicyBased&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;active_active&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;enable_bgp&lt;/span&gt;          &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;sku&lt;/span&gt;                 &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Basic&quot;&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;ip_configuration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;                          &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Gateway&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;public_ip_address_id&lt;/span&gt;          &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_public_ip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;private_ip_address_allocation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Dynamic&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;subnet_id&lt;/span&gt;                     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_subnet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_virtual_network_gateway_connection&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;home_lab&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;                &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;HomeLab&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resource_group_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt;                       &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;IPsec&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;virtual_network_gateway_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_virtual_network_gateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;local_network_gateway_id&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_local_network_gateway&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;home_lab&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;shared_key&lt;/span&gt;                 &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;SHARED_KEY&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;azurerm_local_network_gateway&lt;/code&gt; resource, we define the local network:
the IP we’ll be configuring the VPN to use and what the local network looks
like. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;azurerm_virtual_network_gateway&lt;/code&gt; represents the primary resource, and
then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;azurerm_virtual_network_gateway_connection&lt;/code&gt; builds the connection to the
local network.&lt;/p&gt;

&lt;p&gt;I made up the shared key using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openssl rand -base64 20&lt;/code&gt;, removing
non-alphanumeric characters as these caused the connection to fail to
authenticate. I couldn’t tell which side had a problem with it. You probably
want to &lt;a href=&quot;https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault&quot;&gt;keep the secret in a Key Vault&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Virtual Network Gateway will take a decent amount of time to be operational
— it took about 25 minutes most times.&lt;/p&gt;

&lt;h2 id=&quot;configuring-the-edgerouter&quot;&gt;Configuring the EdgeRouter&lt;/h2&gt;

&lt;p&gt;Connect over SSH and then in configuration mode:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;configure
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Followed by configuring the firewall rules, IKE/ESP groups, then setup the
connection on both sides:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;set vpn ipsec auto-firewall-nat-exclude enable

set vpn ipsec ike-group FOO0 key-exchange ikev1
set vpn ipsec ike-group FOO0 lifetime 28800
set vpn ipsec ike-group FOO0 proposal 1 dh-group 2
set vpn ipsec ike-group FOO0 proposal 1 encryption aes256
set vpn ipsec ike-group FOO0 proposal 1 hash sha1

set vpn ipsec esp-group FOO0 lifetime 3600
set vpn ipsec esp-group FOO0 pfs disable
set vpn ipsec esp-group FOO0 proposal 1 encryption aes256
set vpn ipsec esp-group FOO0 proposal 1 hash sha1

set vpn ipsec site-to-site peer AZURE_GATEWAY_IP authentication mode pre-shared-secret
set vpn ipsec site-to-site peer AZURE_GATEWAY_IP authentication pre-shared-secret SHARED_KEY
set vpn ipsec site-to-site peer AZURE_GATEWAY_IP connection-type respond
set vpn ipsec site-to-site peer AZURE_GATEWAY_IP description ipsec
set vpn ipsec site-to-site peer AZURE_GATEWAY_IP local-address 0.0.0.0

set vpn ipsec site-to-site peer AZURE_GATEWAY_IP ike-group FOO0
set vpn ipsec site-to-site peer AZURE_GATEWAY_IP tunnel 1 esp-group FOO0
set vpn ipsec site-to-site peer AZURE_GATEWAY_IP tunnel 1 local prefix 10.1.0.0/24
set vpn ipsec site-to-site peer AZURE_GATEWAY_IP tunnel 1 remote prefix 10.0.0.0/22
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;commit ; save
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Setting the EdgeRouter to listen on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.0.0.0&lt;/code&gt; rather than the public IP allows
us to work around the public IP not being configured — and helpfully means we
wouldn’t need to reconfigure the EdgeRouter when the public IP changes.&lt;/p&gt;

&lt;h2 id=&quot;testing-the-connection&quot;&gt;Testing the Connection&lt;/h2&gt;

&lt;p&gt;After some time, the connection should hopefully be established. Azure will
report back if it doesn’t work, and it’s possible to view the connection status
on the EdgeRouter by using&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;show vpn status
show vpn sa
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can see the connection state in the Azure portal, too:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/site-to-site-vpn-azure-connection.png&quot; alt=&quot;A screenshot showing the Azure portal, showing the connection status&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;A screenshot showing the Azure portal, showing the connection status&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;VPNs can be notoriously difficult to debug when they go wrong. I’d start by
checking the configuration on either side, then randomly changing things until
it works, if it doesn’t eventually come up. Good luck.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>On to Mastodon</title>
        <link href="https://nickcharlton.net/posts/on-to-mastodon.html" />
        <id>https://nickcharlton.net/posts/on-to-mastodon.html</id>
        <published>Sun, 20 Nov 2022 00:00:00 +0000</published>
        <updated>Sun, 20 Nov 2022 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I’m now using Mastodon: &lt;a href=&quot;https://mastodon.nickcharlton.net/@nick&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@nick@nickcharlton.net&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Some time ago, I stopped contributing on Twitter as much as possible. I was
still obsessively scrolling and using it to keep up with stuff going on, but I
was finding people elsewhere, ideally through subscribing to people’s blogs via
RSS. I limited myself to occasionally liking tweets (I’d always used this as
bookmarking for later and the occasional DM for meeting up with people at
events, too).&lt;/p&gt;

&lt;p&gt;As &lt;a href=&quot;https://ez.substack.com/p/the-fraudulent-king&quot;&gt;Twitter seemed to be starting a terminal decline&lt;/a&gt;, I thought this was
great, and I’d not bother trying to replace it with something else. Maybe
finally I’d lose the thing I find myself obsessively scrolling every day.&lt;/p&gt;

&lt;p&gt;But two weeks ago, I changed my mind. I always thought Mastodon was quite neat,
but if not enough people I’m following on Twitter started to use it, it was
always a bit moot. &lt;a href=&quot;https://adactio.com/journal/19650&quot;&gt;Over the last few weeks, this changed&lt;/a&gt; and &lt;a href=&quot;https://www.hughrundle.net/home-invasion/&quot;&gt;lots of
people started joining&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I feel strongly about retaining control over the things you produce. For
example, this site uses &lt;a href=&quot;https://www.netlify.com&quot;&gt;Netlify&lt;/a&gt;. Still, I have control over the domain and
moving it elsewhere is relatively trivial, if not just a little annoying if I
have to in a hurry. But if I did, nothing significant would change for those
visiting or reading many of the things I have written over the years. &lt;a href=&quot;https://til.simonwillison.net/mastodon/custom-domain-mastodon&quot;&gt;If I
could run Mastodon myself&lt;/a&gt;, it was probably worth the effort if it was
going to take off.&lt;/p&gt;

&lt;p&gt;None of this was happening in isolation. With &lt;a href=&quot;https://xeiaso.net/blog/heroku-devex-2022-05-12&quot;&gt;Heroku’s slow death&lt;/a&gt; and then
the &lt;a href=&quot;https://xeiaso.net/blog/rip-heroku&quot;&gt;removal of their free plan&lt;/a&gt;, I was already migrating off the old and
onto the new. Some of my projects have gone to &lt;a href=&quot;https://fly.io&quot;&gt;Fly&lt;/a&gt;, but I have access to
a bunch of Azure credits, and I was keen to try and spin up a Kubernetes
cluster there as it’s &lt;a href=&quot;https://nickcharlton.net/posts/kubernetes-terraform-google-cloud.html&quot;&gt;been a long time since I tried to do that&lt;/a&gt;.
&lt;a href=&quot;https://github.com/mastodon/mastodon&quot;&gt;Mastodon is a Rails app&lt;/a&gt;, after all, and I’m &lt;a href=&quot;https://thoughtbot.com&quot;&gt;quite familiar with
those&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I plan to write up a lot of the configuration (it did take me about two weeks
to get to this point, fitting in bits here and there), but I make a lot of
notes as I go along. &lt;a href=&quot;https://mastodon.nickcharlton.net/@nick&quot;&gt;But join me on Mastodon, maybe?&lt;/a&gt;&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Site-wide configuration with Administrate</title>
        <link href="https://nickcharlton.net/posts/site-wide-configuration-with-administrate.html" />
        <id>https://nickcharlton.net/posts/site-wide-configuration-with-administrate.html</id>
        <published>Thu, 17 Nov 2022 00:00:00 +0000</published>
        <updated>Thu, 17 Nov 2022 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I haven’t been doing too much Rails recently, but I did do this and it’s
worked out really well. It was one of those situations where you can go,
“oh right! We can do this…” and put together an example in less than an hour
which are always very satisfying.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Adding site-wide configuration with a Rails model can be quite easy to do
with Administrate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://thoughtbot.com/blog/site-wide-configuration-with-administrate&quot;&gt;See on Giant Robots&lt;/a&gt;&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Self-updating GitHub README</title>
        <link href="https://nickcharlton.net/posts/self-updating-github-readme.html" />
        <id>https://nickcharlton.net/posts/self-updating-github-readme.html</id>
        <published>Mon, 10 Oct 2022 00:00:00 +0000</published>
        <updated>Mon, 10 Oct 2022 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Back in 2020, &lt;a href=&quot;https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/managing-your-profile-readme&quot;&gt;GitHub added profile READMEs&lt;/a&gt;. I jumped on it and created
mine when they silently launched the feature but then did …nothing with it.&lt;/p&gt;

&lt;p&gt;As the profile README is implemented as a special repository which gets
rendered on your profile page, I’d wanted to build something that would
auto-update and show contributions after &lt;a href=&quot;https://simonwillison.net/2020/Jul/10/self-updating-profile-readme/&quot;&gt;reading that Simon Willison had done
the same&lt;/a&gt;. I was never going to remember to update these myself, but as it’s
just a GitHub repo, we can use GitHub Actions to make it possible.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/github-readme.png&quot; alt=&quot;Screenshot showing a GitHub README with contributions and blog posts&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;GitHub README with contributions and blog posts&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;fetching-recent-cross-github-activity&quot;&gt;Fetching recent cross-GitHub activity&lt;/h2&gt;

&lt;p&gt;I often make contributions across various different repos and I thought it’d be
interesting to show these off. I might be working on something quite
interesting at work and usually when I do so, I end up opening issues or
contributing PRs to various things. I started off by trying to use the &lt;a href=&quot;https://docs.github.com/en/graphql&quot;&gt;GitHub
GraphQL API&lt;/a&gt;, but the complexity was a lot for a query which was better off
calling the &lt;a href=&quot;https://docs.github.com/en/rest/search&quot;&gt;Search API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I already have a &lt;a href=&quot;https://github.com/pulls?q=is%3Aopen+author%3Anickcharlton+archived%3Afalse+sort%3Aupdated-desc&quot;&gt;search I use to check what open issues and PRs I have&lt;/a&gt;
that I should follow up with, so that was where I started:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;is:open author:nickcharlton archived:false sort:updated-desc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We want archived items (to see closed or merged issues/PRs), but otherwise
that’s what we pass through to the API:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;User-Agent&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Recent GitHub Contributions (&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;Accept&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;application/vnd.github+json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Excon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://api.github.com/search/issues&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;headers: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;query: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;q&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;author:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; sort:updated-desc is:public&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;per_page: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;From here, I’m repacking as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Contribution&lt;/code&gt; object (which is just a PORO with
the keys below) so that it’s a little easier to work with later on:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;symbolize_names: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Contribution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;id: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;title: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;url: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:html_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;state: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;type: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;has_key?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:pull_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;pull_request&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;issue&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;created_at: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:created_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;updated_at: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:updated_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;One deliberate decision was to remove the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GITHUB_TOKEN&lt;/code&gt; from the request
entirely to avoid the possibility of leaking private information. I have access
to a lot of repos and I wouldn’t want to accidentally leak something that
shouldn’t be. Fortunately, GitHub allows enough public access without a token.&lt;/p&gt;

&lt;p&gt;You can &lt;a href=&quot;https://github.com/nickcharlton/nickcharlton/blob/6ece45c2791a4197e047beedb9e97b5a008f6f20/lib/github_contributions.rb&quot;&gt;see the full implementation on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;pulling-in-an-rss-feed&quot;&gt;Pulling in an RSS feed&lt;/h2&gt;

&lt;p&gt;This one is relatively straightforward. My blog has an Atom feed and we can
pull that in and fetch the top five posts. I used &lt;a href=&quot;https://github.com/feedjira/feedjira&quot;&gt;Feedjira&lt;/a&gt;, which seems to
be a well maintained RSS/Atom library which is able to handle malformed feeds
well to avoid other problems:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Excon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://nickcharlton.net/atom.xml&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;feed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Feedjira&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;feed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Contribution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;id: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;title: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;url: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;links&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;state: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;type: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;created_at: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;published&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;updated_at: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;updated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I figured re-using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Contribution&lt;/code&gt; object would be good enough — maybe it’d
be nice to make the GitHub contribution specific fields default to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt; but
this will do.&lt;/p&gt;

&lt;p&gt;You can &lt;a href=&quot;https://github.com/nickcharlton/nickcharlton/blob/6ece45c2791a4197e047beedb9e97b5a008f6f20/lib/rss_feed.rb&quot;&gt;see the full implementation on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;assembling-the-readme&quot;&gt;Assembling the README&lt;/h2&gt;

&lt;p&gt;This was much more fun to put together. In Simon’s original, he used an HTML
comment to surround a block to replace and I did the same. So we’ll have the
following in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;README.md&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- contributions starts --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- contributions ends --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Elsewhere, we’ll assemble the line from the list of contributions and then we
can replace the text:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;replacement&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;REPLACEMENT&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
  &amp;lt;!-- &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;marker&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; starts --&amp;gt;
  &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
  &amp;lt;!-- &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;marker&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; ends --&amp;gt;
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;REPLACEMENT&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gsub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;sr&quot;&gt;/&amp;lt;!\-\- &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;marker&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt; starts \-\-&amp;gt;.*&amp;lt;!\-\- &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;marker&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt; ends \-\-&amp;gt;/m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;replacement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;chomp&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’re able to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;marker&lt;/code&gt; to use the same code to update multiple
sections, which is nice. You can &lt;a href=&quot;https://github.com/nickcharlton/nickcharlton/blob/6ece45c2791a4197e047beedb9e97b5a008f6f20/lib/readme.rb&quot;&gt;see the full implementation on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;building-using-github-actions&quot;&gt;Building using GitHub Actions&lt;/h2&gt;

&lt;p&gt;GitHub Actions has a bunch of nice features we can use here. To start with,
Actions are able to make changes to the repository that they’re running on, so
we can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; commands without configuring more than the name and email
address. Next, we can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;schedule&lt;/code&gt; event to regularly trigger an update.
I chose daily at 11am — I don’t make enough contributions for any more to be
beneficial. I also configured &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;workflow_dispatch&lt;/code&gt; so that I could manually
trigger it if I so desired.&lt;/p&gt;

&lt;p&gt;I had a bit of a chicken and egg problem in doing this. I wanted to test the
Action on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;push&lt;/code&gt; to make sure it was working, but didn’t want the README
changes to get picked up when merging. I ended up making sure it works, then
deleting that commit, removing the push trigger and running it manually after.
Anyway, here’s how it works:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Update README&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;workflow_dispatch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;cron&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;*&apos;&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/checkout@v2&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Set up Ruby&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ruby/setup-ruby@v1&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;bundler-cache&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Install dependencies&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;bundle install&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Update README&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|-&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;bin/update_readme&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;cat README.md&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Commit and push if changed&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|-&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;git diff&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;git config --global user.email &quot;actions@users.noreply.github.com&quot;&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;git config --global user.name &quot;README Bot&quot;&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;git add -A&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;git commit -m &quot;Updated content&quot; || exit 0&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;git push&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So now I have an auto-updating GitHub README which shows up on my profile. &lt;a href=&quot;https://github.com/nickcharlton&quot;&gt;Go
take a look&lt;/a&gt;!&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Flashing the firmware in an IKEA BEKANT to add position memory</title>
        <link href="https://nickcharlton.net/posts/flashing-the-firmward-in-an-ikea-bekant.html" />
        <id>https://nickcharlton.net/posts/flashing-the-firmward-in-an-ikea-bekant.html</id>
        <published>Sun, 09 Oct 2022 00:00:00 +0000</published>
        <updated>Sun, 09 Oct 2022 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;As I started to work from home at the beginning of the pandemic, I got an IKEA
BEKANT sit/stand desk. It’s been great for my posture and managing the fatigue
of being at a desk for a long time every day. My main frustration has always
been that it doesn’t have a memory — you can only hold the up or down buttons
to move it and so trying to get it to the same position is annoying enough to
avoid doing it.&lt;/p&gt;

&lt;p&gt;Fortunately, someone &lt;a href=&quot;https://github.com/ivanwick/bekantfirmware&quot;&gt;reverse engineered the firmware and published a
replacement which can be flashed on the existing hardware&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The BEKANT controller is a &lt;a href=&quot;https://www.microchip.com/PIC16F1938&quot;&gt;PIC microcontroller&lt;/a&gt;, so you’ll need a PICkit
programmer to flash it. &lt;a href=&quot;https://www.amazon.co.uk/gp/product/B098D3VMDP&quot;&gt;I got one of these&lt;/a&gt;, which was about £20 a few
months before doing it. You’ll also need the Microchip environment, &lt;a href=&quot;https://www.microchip.com/en-us/tools-resources/develop/mplab-x-ide&quot;&gt;MPLAB
X&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;preparing-the-controller&quot;&gt;Preparing the controller&lt;/h2&gt;

&lt;p&gt;First, I set the desk to the right sitting height. If it went wrong, it was
much better to have it lower. You’re going to want to do something like:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Power down the desk,&lt;/li&gt;
  &lt;li&gt;Unscrew the controller from underneath,&lt;/li&gt;
  &lt;li&gt;Open up the back&lt;/li&gt;
&lt;/ol&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/ikea-bekant-controller.jpeg&quot; alt=&quot;Photograph showing the start of opening up the controller&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;IKEA BEKANT Controller: Opening&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;It’s quite difficult to open the back. It’s glued down and made me have some
grumpy thoughts about right to repair. I first tried to use a knife to get some
space, but this didn’t work too well. From seeing photographs of others doing
this, I realised there was space unused in the case where I should be safe to
drill into. I drilled three holes into the corner and then used some pliers and
snips to break it open.&lt;/p&gt;

&lt;p&gt;You’ll see something that looks like this.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/ikea-bekant-controller-back-cover-removed.jpeg&quot; alt=&quot;Photograph of the back cover removed&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;IKEA BEKANT Controller: Back Cover Removed&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;configuring-mplab&quot;&gt;Configuring MPLAB&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Open the MPLAB IPE,&lt;/li&gt;
  &lt;li&gt;Configure for the PICkit 3.5
    &lt;ul&gt;
      &lt;li&gt;Settings → Advanced Mode (the default password is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;microchip&lt;/code&gt;),&lt;/li&gt;
      &lt;li&gt;Set the device to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PIC16LF1938&lt;/code&gt; and click “Apply”,&lt;/li&gt;
      &lt;li&gt;Under “Power”, select “Power target circuit from PICkit3” and set the
voltage level to “3.25”,&lt;/li&gt;
      &lt;li&gt;Under “Production”, select “Allow Export Hex”,&lt;/li&gt;
      &lt;li&gt;Then logout from Advanced mode&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;connecting-the-programmer&quot;&gt;Connecting the programmer&lt;/h2&gt;

&lt;p&gt;You’re going to need to set the pins correctly, &lt;a href=&quot;https://github.com/ivanwick/bekantfirmware/wiki/PICkit3#icsp-pin-connection&quot;&gt;this diagram from the docs
should be enough to figure it out&lt;/a&gt;:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/ikea-bekant-icsp-connection.png&quot; alt=&quot;Diagram of the pin connection between controller and PICkit&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Pin connection between the controller and PICkit&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/ikea-bekant-pins-set.jpeg&quot; alt=&quot;Photograph of the pins correctly set on the controller&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Connecting the pins correctly&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Then, Click “Connect” in the main UI, and the programmer will attempt to
connect to the chip.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/ikea-bekant-mplab-connecting.png&quot; alt=&quot;Screenshot of MPLAB connecting to the controller&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;MPLAB: Connecting&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I had some problems in ensuring there was a good connection. Leaning my finger
on the connector was good enough to solve that.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/ikea-bekant-leaning-on-pickit.jpeg&quot; alt=&quot;Photograph of my finger pushing against the PICkit pins to
            ensure a good connection&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;My finger pushing the PICkit pins to ensure a good connection&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;reading-off-the-current-firmware&quot;&gt;Reading off the current firmware&lt;/h2&gt;

&lt;p&gt;Next, read off the existing firmware in case it goes horribly wrong.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/ikea-bekant-mplab-reading-firmware.png&quot; alt=&quot;Screenshot of MPLAB reading the existing firmware&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Screenshot of MPLAB reading the existing firmware&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;a href=&quot;/resources/ikea-bekant-original-firmware.hex&quot;&gt;You can find the firmware from mine here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;flashing-the-new-firmware&quot;&gt;Flashing the new firmware&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Fetch the firmware from the &lt;a href=&quot;https://github.com/ivanwick/bekantfirmware/releases/&quot;&gt;releases page&lt;/a&gt;,&lt;/li&gt;
  &lt;li&gt;Load the firmware (“Hex File” → “Browse),&lt;/li&gt;
  &lt;li&gt;Program it: Click “Program”,&lt;/li&gt;
  &lt;li&gt;Verify it: Click “Verify”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It should program and then verify successfully.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/ikea-bekant-mplab-flashed-firmware.png&quot; alt=&quot;Screenshot of MPLAB after flashing and verifying the firmware&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;MPLAB: Flashing and verify the new firmware&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;testing&quot;&gt;Testing&lt;/h2&gt;

&lt;p&gt;To test it out, I first followed the advice in the docs and cleared enough
space for safety and then wired everything up in a way I could quick disconnect
it if things went wrong.&lt;/p&gt;

&lt;p&gt;Then &lt;a href=&quot;https://github.com/ivanwick/bekantfirmware/wiki/Installation-Guide#Test-new-firmware&quot;&gt;I followed the rest of the guide&lt;/a&gt; which involved testing out the
default buttons, followed by the memory the firmware has by default. Everything
worked out great! I can now press Up whilst pressing Down to move to the higher
position and Down whilst pressing Up to move down. The original commands all
work too.&lt;/p&gt;

&lt;p&gt;All that was left to do was to mount the control unit back to the desk (this
time without the top cover).&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Using HTTPListener to build a HTTP Server in C#</title>
        <link href="https://nickcharlton.net/posts/using-httplistener-to-build-a-http-server-in-csharp.html" />
        <id>https://nickcharlton.net/posts/using-httplistener-to-build-a-http-server-in-csharp.html</id>
        <published>Fri, 29 Apr 2022 00:00:00 +0000</published>
        <updated>Fri, 29 Apr 2022 00:00:00 +0000</updated>
        <summary type="html">&lt;blockquote&gt;
  &lt;p&gt;I needed an HTTP server to test out a software integration on Windows.
Nothing was quite basic enough to just echo out the responses, so I wrote one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://thoughtbot.com/blog/using-httplistener-to-build-a-http-server-in-csharp&quot;&gt;See on Giant Robots&lt;/a&gt;&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #31</title>
        <link href="https://nickcharlton.net/posts/week-notes-31.html" />
        <id>https://nickcharlton.net/posts/week-notes-31.html</id>
        <published>Wed, 02 Mar 2022 00:00:00 +0000</published>
        <updated>Wed, 02 Mar 2022 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;I’ve been jumping back into open source recently, plus doing a lot of
technical writing for work,&lt;/li&gt;
  &lt;li&gt;I &lt;a href=&quot;https://github.com/thoughtbot/administrate/releases/tag/v0.17.0&quot;&gt;released v0.17.0 of Administrate&lt;/a&gt; the other week, which catches up on
a whole load of contributions after nearly a year,&lt;/li&gt;
  &lt;li&gt;Next is going to be a big breaking change, as we remove a lot of
dependencies (like &lt;a href=&quot;https://github.com/thoughtbot/administrate/pull/2136&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;datetime_picker_rails&lt;/code&gt;&lt;/a&gt;) and make the biggest change
so far to how assets are handled. We’ll likely be removing Sass completely
and moving to standard CSS, which I’m quite excited about, but the big news
is that it’ll work much nicer with all of the Rails asset changes which have
happened recently,&lt;/li&gt;
  &lt;li&gt;Technical writing is hard — I’ve been doing a bunch of it, and trying to
distill down a lot of the way receipt printers work so that others’ can read
one guide. I’m hoping this will turn into a series of blog posts, but that’s
going to take much longer!&lt;/li&gt;
  &lt;li&gt;I enjoyed
&lt;a href=&quot;https://mailchi.mp/railsspeed/on-the-various-oss-fauna?e=02a16b330e&quot;&gt;the latest Speedshop newsletter on different styles of open source contribution&lt;/a&gt;,&lt;/li&gt;
  &lt;li&gt;Finally, last week I went to see &lt;a href=&quot;https://wolfalice.co.uk&quot;&gt;Wolf Alice&lt;/a&gt;. Standing in a crowd of
people listening to music is great.&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Converting Unix Shell Aliases to PowerShell</title>
        <link href="https://nickcharlton.net/posts/converting-unix-shell-aliases-to-powershell.html" />
        <id>https://nickcharlton.net/posts/converting-unix-shell-aliases-to-powershell.html</id>
        <published>Thu, 10 Feb 2022 00:00:00 +0000</published>
        <updated>Thu, 10 Feb 2022 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I’ve been using Windows a lot recently and have been quite enjoying it. Some
things are new, but most are old and familiar. But after using a lot of shell
aliases over the last decade of using Linux &amp;amp; macOS, I needed them to come
over with me. I’ve been using &lt;a href=&quot;https://github.com/PowerShell/PowerShell&quot;&gt;PowerShell&lt;/a&gt; 7, inside &lt;a href=&quot;https://github.com/Microsoft/Terminal&quot;&gt;Windows Terminal&lt;/a&gt;
(and mostly &lt;a href=&quot;https://github.com/microsoft/vscode&quot;&gt;VS Code&lt;/a&gt; as the editor), and so far have pulled over my
&lt;a href=&quot;https://github.com/nickcharlton/dotfiles/blob/cead1b533bc154e0810197afeb33e5143f4ee4de/aliases#L10-L31&quot;&gt;Git aliases&lt;/a&gt;. If you’re trying to bring over your own, this should be
enough to get you going.&lt;/p&gt;

&lt;p&gt;PowerShell has an equivalent profile configuration file which can be found in:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C:\Users\&amp;lt;username&amp;gt;\Documents\PowerShell\Microsoft.PowerShell_profile.ps1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…and available as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$profile&lt;/code&gt;. From here, you can configure aliases and
functions which are loaded across each session.&lt;/p&gt;

&lt;p&gt;There’s a couple of nuances in converting existing aliases to PowerShell
though, as an example I’m converting the following which covers all of the
cases I’ve seen so far:&lt;/p&gt;

&lt;p&gt;In Unix Shell:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;alias &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;gws&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;git status -sb&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;alias &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;gl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;gll -20&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;alias &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;gll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;git log --pretty=&apos;format:%C(yellow)%h %C(blue)%ad %C(reset)%s%C(red)%d %C(green)%an%C(reset), %C(cyan)%ar&apos; --date=short&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In PowerShell:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-GitStatus&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-sb&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-GitLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$limit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--pretty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;format:%C(yellow)%h %C(blue)%ad %C(reset)%s%C(red)%d %C(green)%an%C(reset), %C(cyan)%ar&apos;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--date&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;short&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$limit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-GitLogShort&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get-GitLog&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;-20&apos;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set-Alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gws&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Get-GitStatus&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set-Alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Get-GitLogShort&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set-Alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gll&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Get-GitLog&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notably, &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_aliases?view=powershell-7.2&quot;&gt;aliases&lt;/a&gt; don’t expand in place, which means you can’t
automatically chain additional arguments. To do this, we need to use a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;function&lt;/code&gt; to wrap what we like to do and then use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$args&lt;/code&gt;
(&lt;a href=&quot;https://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_02.html&quot;&gt;a special argument equivalent to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$@&lt;/code&gt;&lt;/a&gt;) to capture anything further. This
is also the case if we want to be explicit about
&lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions?view=powershell-7.2#functions-with-parameters&quot;&gt;requiring further arguments&lt;/a&gt;, too.&lt;/p&gt;

&lt;p&gt;By convention, &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_advanced?view=powershell-7.2&quot;&gt;functions are named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Verb-Noun&lt;/code&gt;&lt;/a&gt;. I’m using a separate call
to &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/set-alias?view=powershell-7.2&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Set-Alias&lt;/code&gt;&lt;/a&gt; to replicate the original pattern I’m copying, but there is
a way to &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_advanced_parameters?view=powershell-7.2#alias-attribute&quot;&gt;define these directly on functions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is my current &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$profile&lt;/code&gt;, which is not without the occasional problem, but
is working well so far:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-Git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$isGit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;symbolic-ref&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;HEAD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$null&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

  &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$isGit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$time_since_last_commit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--pretty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;format:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;%ar&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write-Output&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Last commit: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;${time_since_last_commit}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--short&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--branch&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-GitStatus&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-sb&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-GitBranch&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;branch&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-vv&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;New-GitCommit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--verbose&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;New-GitCommitAll&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--verbose&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--all&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;New-GitFixupCommit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--fixup&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;HEAD&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;New-GitCheckout&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;checkout&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;New-GitAddition&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;New-GitPush&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;New-GitForcePush&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--force-with-lease&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;New-GitRebase&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rebase&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;New-GitStash&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stash&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-GitDiff&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;diff&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--no-ext-diff&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-GitLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$limit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--pretty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;format:%C(yellow)%h %C(blue)%ad %C(reset)%s%C(red)%d %C(green)%an%C(reset), %C(cyan)%ar&apos;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--date&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;short&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$limit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-GitLogShort&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get-GitLog&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;-20&apos;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set-Alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Get-Git&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set-Alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gws&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Get-GitStatus&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set-Alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gb&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Get-GitBranch&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set-Alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Value&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;New-GitCommit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Force&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set-Alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gca&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;New-GitCommitAll&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set-Alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gcf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;New-GitFixupCommit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set-Alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gco&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;New-GitCheckout&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set-Alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gia&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;New-GitAddition&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set-Alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Value&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;New-GitPush&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Force&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set-Alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gpf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;New-GitForcePush&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set-Alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;New-GitRebase&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set-Alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;New-GitStash&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set-Alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gwd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Get-GitDiff&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set-Alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Value&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Get-GitLogShort&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Force&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set-Alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gll&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Get-GitLog&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #30</title>
        <link href="https://nickcharlton.net/posts/week-notes-30.html" />
        <id>https://nickcharlton.net/posts/week-notes-30.html</id>
        <published>Sun, 09 Jan 2022 00:00:00 +0000</published>
        <updated>Sun, 09 Jan 2022 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;A New Year always brings a new set of optimism, plans and possibilities for
me and this year is no different,&lt;/li&gt;
  &lt;li&gt;Last year was another rough one (who knew living through a pandemic would be
extremely difficult, even if you never got sick), but we made it,&lt;/li&gt;
  &lt;li&gt;A lovely Christmas and New Year, then back where I was previously; trying to
get my head around Windows, C# and a lot of receipt printer minutiae,&lt;/li&gt;
  &lt;li&gt;Later this year, I’m doing the &lt;a href=&quot;https://frontier300.cc&quot;&gt;Frontier 300&lt;/a&gt;, but I need to get my
fitness well up from where it is currently so I got a turbo trainer, a
&lt;a href=&quot;https://uk.wahoofitness.com/devices/indoor-cycling/bike-trainers/kickr-snap&quot;&gt;Wahoo Kickr Snap&lt;/a&gt; which I started using this weekend, it’s excellent,&lt;/li&gt;
  &lt;li&gt;My plan is to use it through winter to get my cycling fitness back up to
where it was, and then hopefully, way ahead of wherever I managed to get it
to previously, probably through &lt;a href=&quot;https://www.zwift.com/uk&quot;&gt;Zwift&lt;/a&gt;. My training plan starts tomorrow!&lt;/li&gt;
  &lt;li&gt;I note that this is the thirtieth one of these I’ve done (and with it
being a new year) feels like a bit of a milestone. I enjoy writing these,
even if I’ve skipped &lt;em&gt;a lot&lt;/em&gt; recently, onwards!&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #29</title>
        <link href="https://nickcharlton.net/posts/week-notes-29.html" />
        <id>https://nickcharlton.net/posts/week-notes-29.html</id>
        <published>Sun, 21 Nov 2021 00:00:00 +0000</published>
        <updated>Sun, 21 Nov 2021 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;A busy couple of months or so, which so easily ends up breaking the habit of
writing something regularly,&lt;/li&gt;
  &lt;li&gt;It’s mostly been work, my current project — which involves building lots of
different bits of software and some hardware integration — has been fairly
intense recently, but I’ve also been learning to drive (which I picked up
again after a 10 year hiatus),&lt;/li&gt;
  &lt;li&gt;This has all lead to my previously delightfully minimalist one computer, one
monitor and tidy desk setup to have another monitor, another computer and
various bits of hardware spread everywhere (two receipt printers, three
laptops with my own counted and an industrial Windows tablet). It’s been
the most interesting and challenging thing I’ve ever worked on,&lt;/li&gt;
  &lt;li&gt;I’ve had more opportunities to write TypeScript, and most recently C# which
is not something I’ve done since University. I’m a fan; especially when tied
with Visual Studio,&lt;/li&gt;
  &lt;li&gt;The most noticeable thing is strongly typed languages when it comes to
refactoring. The initial process of getting going might well be slower, but
the middle of a project and onwards is so much easier,&lt;/li&gt;
  &lt;li&gt;I’ve also ended up reading a bunch of technical books lately:
&lt;a href=&quot;https://uk.bookshop.org/books/domain-driven-design-distilled/9780134434421&quot;&gt;&lt;em&gt;Domain Driven Design Distilled&lt;/em&gt;&lt;/a&gt; (I’d recommend it),
&lt;a href=&quot;https://uk.bookshop.org/books/building-event-driven-microservices-leveraging-organizational-data-at-scale/9781492057895&quot;&gt;&lt;em&gt;Building Event-Driven Microservices&lt;/em&gt;&lt;/a&gt; (I’m about half way through, and
using DDD to think about breaking up services through events seems to fit so
nicely), and &lt;a href=&quot;https://uk.bookshop.org/books/c-in-depth-4e/9781617294532&quot;&gt;&lt;em&gt;C# In Depth&lt;/em&gt;&lt;/a&gt; (although this was a bit too …in depth so
far),&lt;/li&gt;
  &lt;li&gt;I’m extremely far behind in all of my open source notifications, partly
through having much less time at thoughtbot whilst we try out a short Friday
experiment, so if you’re waiting on something, sorry about that,&lt;/li&gt;
  &lt;li&gt;Finally, I got around to upgrading my Mac to Big Sur (a year after it was
released) and thought sod-it and immediately jumped onto the Monterey beta
with the RC. It’s been great so far, so 🤞&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Switching to FeedBin and NetNewsWire</title>
        <link href="https://nickcharlton.net/posts/switching-to-feedbin-and-netnewswire.html" />
        <id>https://nickcharlton.net/posts/switching-to-feedbin-and-netnewswire.html</id>
        <published>Sun, 21 Nov 2021 00:00:00 +0000</published>
        <updated>Sun, 21 Nov 2021 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I’ve been using FeedWrangler and Reeder since &lt;a href=&quot;https://en.wikipedia.org/wiki/Google_Reader&quot;&gt;Google Reader&lt;/a&gt; shutdown. All
of these years they’ve continued to work really well. But I’ve wanted to move
back to NetNewsWire since it was taken back over by Brent Simmons and made Open
Source.&lt;/p&gt;

&lt;p&gt;NetNewsWire was always more of a “Mac app” than Reeder ever intended to be,
which played around with new UI ideas and helped push along some of the more
innovative patterns we see today. But two things always bugged me:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;It has no feed organisation, you just have one long list of unread items,&lt;/li&gt;
  &lt;li&gt;The swipe gesture and I never really got along as I’d accidentally jump
around whilst reading,&lt;/li&gt;
  &lt;li&gt;The keyboard shortcuts always surprised me, being always slightly different&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;NetNewsWire solves all of these. But it doesn’t support FeedWrangler.
FeedWrangler has been great; I jumped on it in the days when Google Reader
announced it was going to shutdown and the frenzy of RSS syncing services
started up. But since those days, it’s broadly stayed the same and is now in a
deliberate stagnant mode.&lt;/p&gt;

&lt;p&gt;I use multiple devices and operating systems, so to switch to NetNewsWire, I
needed a different syncing service. I was keen on something that:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Restored the web reading experience from the days of Google Reader,&lt;/li&gt;
  &lt;li&gt;Something that would be around for a long time,&lt;/li&gt;
  &lt;li&gt;Supported NetNewsWire (…obviously)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I ended up picking Feedbin. It’s &lt;a href=&quot;https://github.com/feedbin/feedbin&quot;&gt;open source&lt;/a&gt; so if I really needed, I
could host it myself. It’s been around for ages and I could pay for it with a
reasonable monthly cost, so I felt reasonably confident it’d be around for much
longer. Plus, the web UI looked great.&lt;/p&gt;

&lt;p&gt;But the most compelling feature to me was &lt;em&gt;Actions&lt;/em&gt;. I use RSS in two ways: to
keep up with news, and to follow people’s blogs. People’s blogs are often
management or technical in nature and so if I read them a few months later it’s
not a big deal. News isn’t like that, and there’s often regular stories I
immediately mark as read. With Feedbin’s Actions, I could automate marking
stuff as read. That’s wonderful.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/feedbin-actions.png&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;An example of Feedbin&apos;s Actions auto-mark as read feature&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Migrating was a bit of a challenge. I don’t keep anything like RSS reader inbox
zero. Mostly recently, I’ve been avoiding it too, as it hasn’t been working
well for me and reading email newsletters much more instead. At the start of
the migration, I had just about 600 unread items. These went back to 2018, but
most were quite recent. It was broadly a case of:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Skim the headlines and mark everything I didn’t care enough about as read,&lt;/li&gt;
  &lt;li&gt;Push longer items to Instapaper for reading later,&lt;/li&gt;
  &lt;li&gt;Open shorter items, videos and everything I wanted to read properly in the
browser,&lt;/li&gt;
  &lt;li&gt;Skim back through and mark more as unread,&lt;/li&gt;
  &lt;li&gt;End up with some short items to skim read&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From here, I could export my list of feeds from FeedWrangler as an OPML file
and import that into Feedbin. Then install NetNewsWire everywhere.&lt;/p&gt;

&lt;p&gt;I did this a few months ago and found myself getting excited about RSS again
and starting to stop the newsletters which end up in my email inbox get back
over to RSS!&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #28</title>
        <link href="https://nickcharlton.net/posts/week-notes-28.html" />
        <id>https://nickcharlton.net/posts/week-notes-28.html</id>
        <published>Mon, 21 Jun 2021 00:00:00 +0000</published>
        <updated>Mon, 21 Jun 2021 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;I’ve spent the last few weekends doing a lot of woodworking. I set myself the
goal this year of building much more for the garden and — mostly because of
the weather, but also because everything takes far longer than you’d expect —
I ended up finishing one of the three planters I’d wanted to build,&lt;/li&gt;
  &lt;li&gt;This first one (pictured below) is for cucumbers: it’s a box with a built-in
trellis (which is thick twine) which they grow up. Now I’m writing this,
they’ve all reached the top and are full of fruit!&lt;/li&gt;
  &lt;li&gt;Unfortunately for my plans for woodworking, the weather changed and it’s been
raining for the past week. But that gave me an empty weekend to chill out
…and make some time to ship a bunch of site changes I started a year ago,&lt;/li&gt;
  &lt;li&gt;Since this time last year, I’ve had a branch called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redesign-2020&lt;/code&gt; which
I’ve repeatedly picked up and put back down again since. But in the spirit of
designing in the open (and also, honestly, making sure I ship something), I
tidied up the branch and just merged it. I’m going to keep fiddling with
small bits from now on to keep it going. I have a long list of bits to fiddle
with and improve and so these will happen gradually over time,&lt;/li&gt;
  &lt;li&gt;Plus, some long running blog posts have some more substance to them now. I’ll
be finishing those up and posting them soon!&lt;/li&gt;
&lt;/ul&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/week-notes-28-cucumber-planter.jpg&quot; alt=&quot;Cucumber planter with plants&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Cucumber planter with plants&lt;/figcaption&gt;
&lt;/figure&gt;

</summary>
    </entry>
    
    <entry>
        <title>Filtering Jekyll Posts by Tag</title>
        <link href="https://nickcharlton.net/posts/filtering-jekyll-posts-by-tag.html" />
        <id>https://nickcharlton.net/posts/filtering-jekyll-posts-by-tag.html</id>
        <published>Sun, 20 Jun 2021 00:00:00 +0000</published>
        <updated>Sun, 20 Jun 2021 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I’m doing a bit of spring cleaning around here, and I wanted to split the list
of posts on the index into &lt;em&gt;Week Notes&lt;/em&gt; and everything else. I differentiate
&lt;em&gt;Week Notes&lt;/em&gt; posts from others by tagging them with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;week-notes&lt;/code&gt; and filtering
by these seemed easily enough. But I was wrong.&lt;/p&gt;

&lt;p&gt;In Jekyll, posts exist in a collection called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;site.posts&lt;/code&gt;, which you could
pipe into a &lt;a href=&quot;https://jekyllrb.com/docs/liquid/filters/&quot;&gt;filter to change the list returned&lt;/a&gt;, so you could get a list
of &lt;em&gt;Week Notes&lt;/em&gt; posts by doing:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;% assign &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;where_exp: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;item&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;item.tags contains &apos;week-notes&apos;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;% for &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;limit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;% endfor &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Unfortunately, you &lt;a href=&quot;https://github.com/Shopify/liquid/issues/138&quot;&gt;can’t negate the expression as it’s not been implemented
and doing so seems like it would break some established patterns&lt;/a&gt;. You
&lt;em&gt;can&lt;/em&gt; do something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unless post.tags contains &apos;week-notes&apos;&lt;/code&gt; inside the
loop, but this would mean we can’t limit the amount of posts we try to render
which is both awkward and inefficient.&lt;/p&gt;

&lt;p&gt;Fortunately, it’s not too difficult to &lt;a href=&quot;https://jekyllrb.com/docs/plugins/filters/&quot;&gt;build your own filter&lt;/a&gt; and so I came
up with:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;% assign &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;filter_posts: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;tags&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;include &apos;week-notes&apos;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…which is similar to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;where_exp&lt;/code&gt;, but more specific to filtering posts; I
didn’t want to get too deep into parsing expressions, so I used a regular
expression and a bit of meta programming to get something which works nicely:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Jekyll&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;FilterPosts&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;filter_posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;method_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;scan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/(\w*)\s?&apos;([\w-]*)?&apos;/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;

      &lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;method_name&lt;/span&gt;
               &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;includes&quot;&lt;/span&gt;
                 &lt;span class=&quot;ss&quot;&gt;:select&lt;/span&gt;
               &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;excludes&quot;&lt;/span&gt;
                 &lt;span class=&quot;ss&quot;&gt;:reject&lt;/span&gt;
               &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
                 &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;
               &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Liquid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register_filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Jekyll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;FilterPosts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Jekyll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;FilterPosts&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Jekyll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;FilterPosts&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tags&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;can filter by presence of tags&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;document1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Jekyll::Document&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;title: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Post 1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                             &lt;span class=&quot;ss&quot;&gt;data: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tags&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;week-notes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;document2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Jekyll::Document&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;title: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Post 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                             &lt;span class=&quot;ss&quot;&gt;data: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tags&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;projects&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filter_posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;document1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                           &lt;span class=&quot;s2&quot;&gt;&quot;tags&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                           &lt;span class=&quot;s2&quot;&gt;&quot;includes &apos;week-notes&apos;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;document1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;can filter by exclusion of tags&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;document1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Jekyll::Document&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;title: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Post 1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                             &lt;span class=&quot;ss&quot;&gt;data: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tags&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;week-notes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;document2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Jekyll::Document&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;title: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Post 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                             &lt;span class=&quot;ss&quot;&gt;data: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tags&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;projects&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filter_posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;document1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                           &lt;span class=&quot;s2&quot;&gt;&quot;tags&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                           &lt;span class=&quot;s2&quot;&gt;&quot;excludes &apos;week-notes&apos;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;document2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;is empty if the filter method is invalid&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;document1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Jekyll::Document&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;title: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Post 1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                             &lt;span class=&quot;ss&quot;&gt;data: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tags&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;week-notes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;document2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Jekyll::Document&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;title: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Post 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                             &lt;span class=&quot;ss&quot;&gt;data: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tags&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;projects&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filter_posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;document1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                           &lt;span class=&quot;s2&quot;&gt;&quot;tags&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                           &lt;span class=&quot;s2&quot;&gt;&quot;something &apos;week-notes&apos;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([])&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Jekyll::Document&lt;/code&gt; has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data&lt;/code&gt; hash which we can ask for information about
the post, so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;attribute&lt;/code&gt; here is calling that. I didn’t test for it, but you
could presumably filter for other things as well as tags. For testing,
&lt;a href=&quot;https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-filters&quot;&gt;you’d usually want to test the output of the filter&lt;/a&gt;, but in this case
it’s a collection and so it seemed much easier to do that directly.&lt;/p&gt;

&lt;p&gt;You can see it all tied together in &lt;a href=&quot;https://github.com/nickcharlton/nickcharlton.net/pull/77&quot;&gt;the PR which added it&lt;/a&gt;.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #27</title>
        <link href="https://nickcharlton.net/posts/week-notes-27.html" />
        <id>https://nickcharlton.net/posts/week-notes-27.html</id>
        <published>Tue, 04 May 2021 00:00:00 +0000</published>
        <updated>Tue, 04 May 2021 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;A long weekend which didn’t quite go to plan: a slow (but necessary)
Saturday, followed by a bad start to some wood working projects (nothing that
bad though!),&lt;/li&gt;
  &lt;li&gt;In the UK we had a public holiday and so this weekend was three days long. I
usually like to use them for starting projects; in three days you can make
some good progress. I’d planned to replace some table legs and build an
indoor cucumber planter: a big box with a trellis built in for the plants to
grow up,&lt;/li&gt;
  &lt;li&gt;But I didn’t get very far into the table legs. I’m bad at getting accurate
cuts with a handsaw and so the initial cuts were close, but not good enough.
I tried to sand the ends down but using a random orbital sander isn’t the
right tool for it and so I had to give up. I did plan out the cucumber
planter, but next weekend I’m going to build a jig for my (soon to arrive!)
circular saw which I’m hoping will solve all my problems. I’ll report back,&lt;/li&gt;
  &lt;li&gt;A work though, a new project has me on &lt;a href=&quot;https://www.jamesshore.com/v2/books/aoad1/iteration_planning&quot;&gt;batman duties (jumping around between
team members helping things move through so lots of pairing, code review and
testing)&lt;/a&gt;. The intention is twofold: one to help me with burn out
(although, this role has it’s own issues in that regard) and to help things
move through faster. It’s hard to tell concretely how improved things have
been, but the general feeling is positive,&lt;/li&gt;
  &lt;li&gt;Finally, I’m trying to stop my endless open tab addiction by starting to use
&lt;a href=&quot;https://www.instapaper.com&quot;&gt;Instapaper&lt;/a&gt; again. For a long time, I’ve opened a lot of browser tabs of
articles during the week, which I’d usually not get through and so would
build up and up. I’d found that Instapaper would become another inbox, but
I’m thinking that by doing a quick weekly review should keep the list down
somewhat. I’ll report back on how well it works!&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #26</title>
        <link href="https://nickcharlton.net/posts/week-notes-26.html" />
        <id>https://nickcharlton.net/posts/week-notes-26.html</id>
        <published>Tue, 20 Apr 2021 00:00:00 +0000</published>
        <updated>Tue, 20 Apr 2021 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;Quite a gap since I last wrote one of these; I recently rotated off a
tough project which was taking up all of my time. But now I’m on something
new and then had a lovely week off!&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://uk.bookshop.org/books/the-food-almanac-recipes-and-stories-for-a-year-at-the-table/9781911641605&quot;&gt;&lt;em&gt;The Food Almanac&lt;/em&gt;&lt;/a&gt; &lt;a href=&quot;https://emailaddress.horse/2021/02/13/i-have-opened-my-window-and-it-is-cold-outside.html&quot;&gt;via John&lt;/a&gt; that’s a collection of seasonal recipes
and stories, which in turn lead to picking up &lt;a href=&quot;https://www.amazon.co.uk/dp/1439181888&quot;&gt;&lt;em&gt;An Everlasting Meal&lt;/em&gt;&lt;/a&gt;
which I last tried in 2013 and didn’t really get on with, but was reminded of
again because of &lt;a href=&quot;https://www.aliciakennedy.news&quot;&gt;Alicia Kennedy’s newsletter&lt;/a&gt; which I always look forward
to reading. As I read it this second time, I’m realising I’ve (accidentally?)
grown as a cook to do a lot of the topics covered, but I’m still learning a
lot and I enjoy the little bits of recipes spread throughout,&lt;/li&gt;
  &lt;li&gt;I’ve been focused on wrapping up stuff I’d started, and a few workflow
optimisations the past few weeks,&lt;/li&gt;
  &lt;li&gt;On my &lt;a href=&quot;https://github.com/nickcharlton/dotfiles&quot;&gt;dotfiles&lt;/a&gt;, I’ve been to removing some dependencies which have been
causing some extra complexity and also trying to make them more portable.
This has meant &lt;a href=&quot;https://github.com/nickcharlton/dotfiles/tree/remove-base16&quot;&gt;removing base16&lt;/a&gt;, which I was using for colours and moving
to something which relies more on the shell itself,&lt;/li&gt;
  &lt;li&gt;Plus I’ve been trying to finish a very long in progress blog post around my
desk and workplace setup which I started last year,&lt;/li&gt;
  &lt;li&gt;I’ve also been planning out wood working projects for this year: replacing
some table legs we use as a kitchen island, building several new planters.
I’ve also been thinking a lot about building outside furniture, after being
inspired by &lt;a href=&quot;https://www.youtube.com/watch?v=RbQGLaLDEA4&quot;&gt;this Laura Kampf video&lt;/a&gt;. I don’t quite have the amount of
tools though!&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #25</title>
        <link href="https://nickcharlton.net/posts/week-notes-25.html" />
        <id>https://nickcharlton.net/posts/week-notes-25.html</id>
        <published>Tue, 09 Mar 2021 00:00:00 +0000</published>
        <updated>Tue, 09 Mar 2021 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;I just finished four sessions of physio: I’d been limping everywhere since
before Christmas, but now I’m all back to running which after so much pain is
excellent,&lt;/li&gt;
  &lt;li&gt;Plus, starting the longer rides again as I start to build up the fitness for
cycling again, but &lt;a href=&quot;https://www.komoot.com/tour/322999135?ref=itd&amp;amp;share_token=ar0OxMvw4rLk61O1zV0OJURZjlaJ2eocvodj5DZlqQSFiihzRC&quot;&gt;mostly so I can spend some time away from the concrete of
London&lt;/a&gt;!&lt;/li&gt;
  &lt;li&gt;It’s been very satisfying to get back into writing — more to come here,&lt;/li&gt;
  &lt;li&gt;I’ve been working a bunch on converting some projects to &lt;a href=&quot;https://github.com/thoughtbot/administrate/pull/1932&quot;&gt;GitHub Actions for
CI: a few of them (particularly Administrate) are fairly slow&lt;/a&gt;, which is
partly the speed of tests itself, but mostly because we just keep sitting in
queues to run the jobs,&lt;/li&gt;
  &lt;li&gt;Second beer, this time a “California Common”, which is close to a lager-style
ale and the first one which really needed &lt;a href=&quot;/posts/building-a-brew-fridge.html&quot;&gt;the Brew Fridge&lt;/a&gt; to make work.&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Making Drafts&apos; Action Bar fit my workflow</title>
        <link href="https://nickcharlton.net/posts/drafts-action-bar-workflow.html" />
        <id>https://nickcharlton.net/posts/drafts-action-bar-workflow.html</id>
        <published>Mon, 08 Mar 2021 00:00:00 +0000</published>
        <updated>Mon, 08 Mar 2021 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I use &lt;a href=&quot;https://getdrafts.com&quot;&gt;Drafts&lt;/a&gt; for capturing text (everything from quick notes to this blog
post started there). I’ve been using Drafts for a year, and over that time,
it’s become integral to my workflow. But I’d not done much to customise it to
fit what I regularly do.&lt;/p&gt;

&lt;p&gt;I typically have a view that looks like this:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://nickcharlton.net/resources/images/drafts-main-window.png&quot; alt=&quot;A screenshot showing the Drafts editor area with the Action
                 Bar shown at the bottom&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Drafts Main window, showing the editor area with Action Bar at the bottom&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I use &lt;em&gt;Flags&lt;/em&gt; to keep many notes at the top of the pile and Workspaces as a
context (but typically show everything in the &lt;em&gt;Inbox&lt;/em&gt;). As I have Drafts take
up a quarter of my display, I always have the Actions sidebar hidden to
maximise note space. But the exciting bit is what I ended up doing with the
&lt;em&gt;Actions Bar&lt;/em&gt;, which shows just below the note itself.&lt;/p&gt;

&lt;p&gt;Outside of just being an excellent editor for notes, the real power of Drafts
is in &lt;em&gt;Actions&lt;/em&gt;. I use very few in practice, but the ones I have, I find myself
using a lot. After having this workflow for a year, I’ve finally gotten a good
idea of what to do and made the &lt;em&gt;Actions Bar&lt;/em&gt; reflect it.&lt;/p&gt;

&lt;p&gt;I started by creating a new group — I just called mine “Default” as it was the
first appropriate word that came to mind — and then moved the most common set
of actions I remember myself using to end up with a list which is:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Tasks (which turns a line into a checkbox: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;- [ ] Item&lt;/code&gt;),&lt;/li&gt;
  &lt;li&gt;Insert Date,&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://actions.getdrafts.com/a/1L4&quot;&gt;Markdown Reference Link&lt;/a&gt;,&lt;/li&gt;
  &lt;li&gt;Markdown Table (generates a table, which I can never remember how to do),&lt;/li&gt;
  &lt;li&gt;Sort (which sorts alphanumerically),&lt;/li&gt;
  &lt;li&gt;Copy as Rich Text,&lt;/li&gt;
  &lt;li&gt;Copy as HTML,&lt;/li&gt;
  &lt;li&gt;Public Gist (this and the next is the &lt;em&gt;&lt;a href=&quot;https://actions.getdrafts.com/a/18O&quot;&gt;Post Gist to GitHub Action&lt;/a&gt;&lt;/em&gt;),&lt;/li&gt;
  &lt;li&gt;Private Gist,&lt;/li&gt;
  &lt;li&gt;Preview (which pops open an HTML preview of the note),&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://actions.getdrafts.com/a/1ah&quot;&gt;Selection to Draft&lt;/a&gt; (which takes the current selection and makes a new
Draft from it; great for breaking out notes),&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://actions.getdrafts.com/a/1CO&quot;&gt;To Things&lt;/a&gt; (which creates an item in &lt;a href=&quot;https://culturedcode.com/things/&quot;&gt;Things&lt;/a&gt; with the title and
description)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://directory.getdrafts.com/a/2B3&quot;&gt;Move Line Up&lt;/a&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Opt-↑&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://directory.getdrafts.com/a/2B4&quot;&gt;Move Line Down&lt;/a&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Opt-↓&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://directory.getdrafts.com/a/1Bw&quot;&gt;Indent Line&lt;/a&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;⌘-]&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://directory.getdrafts.com/a/1By&quot;&gt;Outdent Line&lt;/a&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;⌘-[&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The ones without links are already part of Drafts. To do this, I moved the
originals from their groups into the new one. It didn’t seem worth doing
anything else more complex.&lt;/p&gt;

&lt;p&gt;The final four I mostly use when outlining or making notes which might cover
lots of topics but which don’t need to be expanded out too much. 1-on-1s are a
good case of these.&lt;/p&gt;

&lt;p&gt;The real power of tools like Drafts is in the way you can customise and then
mix-and-match scripts to do stuff for you. But also, this is perhaps your
general reminder to go back to evaluate what you use regularly and optimise it
for how you use it now!&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Building a Brew Fridge</title>
        <link href="https://nickcharlton.net/posts/building-a-brew-fridge.html" />
        <id>https://nickcharlton.net/posts/building-a-brew-fridge.html</id>
        <published>Wed, 24 Feb 2021 00:00:00 +0000</published>
        <updated>Wed, 24 Feb 2021 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I started brewing beer about three years ago and got to the stage where I was
doing full-grain brewing and was really into it. But then I moved house and
…just stopped.&lt;/p&gt;

&lt;p&gt;Over those past few years I’ve been living in two (broadly identical) draughty
old Victorian houses with nowhere appropriate to leave a fermenter in. With
these kind of houses, the temperature fluctuates massively throughout the day
and so the first brew was a bit of a disaster. It fermented &lt;em&gt;fine&lt;/em&gt; but just
didn’t taste very good and eventually most of it went down the drain.&lt;/p&gt;

&lt;p&gt;I wanted to take this environmental restriction out of the equation and had
read a lot about people converting fridges and freezers to control fermentation
temperature. Did it solve the problem? Yes! I’m now two successful brews in.
Here’s what I did…&lt;/p&gt;

&lt;h2 id=&quot;scope&quot;&gt;Scope&lt;/h2&gt;

&lt;p&gt;Given a fridge and a heating element, the idea is to provide a temperature
regulated environment to store your fermenter.&lt;/p&gt;

&lt;p&gt;This whole idea started with discovering the &lt;a href=&quot;https://www.brewpi.com&quot;&gt;BrewPi&lt;/a&gt; way back when I
started brewing all of those years ago. The BrewPi allows you to control and
visualise the temperature with the combination of a bunch of relays and
thermocouples. It has a few guides and is presented as a nice looking product.
It’s a very neat device, but it’s also &lt;a href=&quot;https://store.brewpi.com/featured/brewpi-spark-3&quot;&gt;quite expensive starting at about
€170&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I needed to reduce the cost, luckily the brewing community has shared lots of
alternative methods. This lead to searching for an off-the-shelf temperature
controller which would do a good enough job of maintaining the temperature in
a generic fridge. I didn’t really need an extremely precise amount of
temperature control (not so much 0.1º, but 1º) because most recipes are in a
wide enough recommended temperature range and that visualising what was going
on would be nice, but not really needed.&lt;/p&gt;

&lt;p&gt;This lead me to finding the &lt;a href=&quot;https://www.amazon.co.uk/dp/B00IJ0F2OW&quot;&gt;ITC-1000 and can be had for about £15&lt;/a&gt;. The
downside with the ITC-1000 is that they still need a bunch of wiring work but
some more research lead to the &lt;a href=&quot;https://www.amazon.co.uk/dp/B018K82UQU&quot;&gt;Inkbird ITC-308&lt;/a&gt; which didn’t. You plug in
your heater in one socket and the fridge into the other. Perfect.&lt;/p&gt;

&lt;h2 id=&quot;choosing-a-fridge&quot;&gt;Choosing a Fridge&lt;/h2&gt;

&lt;p&gt;This left the biggest expense: a fridge.&lt;/p&gt;

&lt;p&gt;I first looked at fridges back in 2018. My main concern was finding a model
that would definitely be big enough, as once you take into account not being
able to use the space around the hump (either because we need to put a heater
in there or because it takes up too much space) the space is quite restricted.
On top of this, it’s quite hard to find fridges with as few features as
possible: similarly, we don’t want an ice box as that too takes up valuable
space.&lt;/p&gt;

&lt;p&gt;My first instinct was to try and reuse one, but not being able to pick one up
myself meant that buying a new one was cheaper. The modern world sucks
sometimes.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/brew-fridge-old-fridge-research-image.jpeg&quot; alt=&quot;Old Fridge Research&quot; max-width=&quot;250px&quot; /&gt;
  &lt;figcaption&gt;Old Fridge Research&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;In the end I settled on a &lt;a href=&quot;https://www.currys.co.uk/gbuk/household-appliances/refrigeration/fridges/essentials-cul55w20-undercounter-fridge-white-10205941-pdt.html&quot;&gt;Currys Essentials CUL55W20&lt;/a&gt;, which at the time
worked out to be £139.99 with free delivery. This wasn’t quite the cheapest
possible, but just about one up. The main difference here is the width, which
means a 30L fermentation bucket fits with room to spare, which with a slimline
one might not have quite worked out.&lt;/p&gt;

&lt;p&gt;As I’d come back to this project a while after first starting, I’d realised
that the model number breaks down to be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;55W&lt;/code&gt;, meaning 55cm wide and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;20&lt;/code&gt; for
the model year. (If you look at the photos above, the model year is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;18&lt;/code&gt;). I
think &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UL&lt;/code&gt; is likely “undercounter larder”.&lt;/p&gt;

&lt;p&gt;I’d always traditionally used &lt;a href=&quot;https://www.themaltmiller.co.uk/product/bubbler-airlock/&quot;&gt;bubbler airlocks&lt;/a&gt;, but these won’t fit with
the amount of vertical space available. I picked up a few &lt;a href=&quot;https://www.themaltmiller.co.uk/product/cylindrical-airlock/&quot;&gt;cylindrical ones&lt;/a&gt;
instead which do fit.&lt;/p&gt;

&lt;h2 id=&quot;fitting-a-heater-and-temperature-controller&quot;&gt;Fitting a Heater and Temperature Controller&lt;/h2&gt;

&lt;p&gt;Now I had a fridge, it was time to work out what else I’d need. A big decision
I’d made early on was to make sure I could use the fridge as it was originally
intended if necessary (over Christmas 2020, it was full of potatoes and then
the turkey!) and so I didn’t want cables just coming through the fridge wall or
to have to remove any material inside the fridge which would stop the shelves
being able to go back in.&lt;/p&gt;

&lt;p&gt;Through a long-lost forum thread, I was inspired to install a waterproof socket
which would leave a surface mount on the back wall and also allow the heater to
be removed if I wanted to.&lt;/p&gt;

&lt;p&gt;For the heater, I went with a &lt;a href=&quot;https://www.amazon.co.uk/dp/B00L41ZVBW&quot;&gt;basic 60W tubular one&lt;/a&gt; — typically used for
outside bathrooms or greenhouses. These come with a bare set of wires, as
they’re designed to go into a fixed fused spur at 3A. I achieved the same thing
by installing a socket on one end to the same amperage.&lt;/p&gt;

&lt;p&gt;The most difficult part of the whole project was working out where to drill the
hole for the socket. &lt;a href=&quot;/posts/a-weekend-with-a-flir-one.html&quot;&gt;I previously wrote about hiring a thermal camera&lt;/a&gt;
and this project is really what it was for. For a less technical route, there’s
a commonly cited method of vodka and corn flour (the idea being that the vodka
evaporates quickly, leaving dry cornflour to highlight the coolant lines) but
I didn’t feel particularly confident with it. So I took a couple of thermal
images to confirm my suspicions of where might be good to drill a hole.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/brew-fridge-thermal-image.jpeg&quot; alt=&quot;Thermal image of the inside of fridge showing hot spots&quot; max-width=&quot;250px&quot; /&gt;
  &lt;figcaption&gt;The inside of a switched on fridge through a thermal camera&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;From looking at some product diagrams of fridges and then from looking at the
fridge itself, I figured that my best bet was to use the hump at the bottom.
This is there to provide a space for the compressor, pump and electronics for
the fridge without it sticking out the back. In addition to this, from looking
at the coolant lines which are visible, it was possible to see these go up into
the back of the fridge (and so above where I wanted to drill). You can see this
happening in action on the thermal image below:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/brew-fridge-thermal-image-fridge-rear.jpeg&quot; alt=&quot;A thermal image showing the hump at the back of a fridge giving off
    heat&quot; max-width=&quot;250px&quot; /&gt;
  &lt;figcaption&gt;
    Thermal image showing the hump at the back of a fridge giving off heat
  &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;This left drilling the actual hole. First, I drilled a 5mm pilot hole through
the outer layer of plastic, then pushed through a long piece of stiff wire to
break through all of the insulation. This ensured I wasn’t about to hit
something I didn’t want to and that the exit hole also wouldn’t hit the
compressor. After this, I used a step drill to go through wide enough to get
the 23mm hole required by the socket. I found the insulation to be much thicker
than you’d expect (which I suppose is not surprising!), but it’s easy to get
through as it’s just foam. You can broadly see the process in the photos below.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/brew-fridge-pilot-hole.jpeg&quot; alt=&quot;A photo showing the base of a fridge, with a pilot hole with wire
    coming through&quot; max-width=&quot;250px&quot; /&gt;
  &lt;figcaption&gt;Pilot Hole, with wire to breach the insulation&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/brew-fridge-drilling.jpeg&quot; alt=&quot;A photo showing the main hole being drilled&quot; max-width=&quot;250px&quot; /&gt;
  &lt;figcaption&gt;Drilling the main hole&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/brew-fridge-heater-flex.jpeg&quot; alt=&quot;A photo showing the heater wire being passed through&quot; max-width=&quot;250px&quot; /&gt;
  &lt;figcaption&gt;Passing through the heater wire&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/brew-fridge-installed-socket.jpeg&quot; alt=&quot;A photo showing the installed socket&quot; max-width=&quot;250px&quot; /&gt;
  &lt;figcaption&gt;Socket installed&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The socket is from &lt;a href=&quot;https://www.rapidonline.com/brands/elkay?Tier=Weatherproof%20Connectors&quot;&gt;the Elkay waterproof range which I got from Rapid
Electronics&lt;/a&gt;, I paired this with some &lt;a href=&quot;https://www.screwfix.com/p/time-3093y-white-3-core-0-75mm-flexible-cable-25m-drum/177jy?_requestid=582403&quot;&gt;0.75mm² flex&lt;/a&gt; which is the same
as used on the heater. The heater itself is relatively generic and just big
enough to cover most of the area at the bottom of the fridge at about 50cm.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/brew-fridge-installed-heater.jpeg&quot; alt=&quot;A tubular heater in the base of a fridge&quot; max-width=&quot;250px&quot; /&gt;
  &lt;figcaption&gt;The tubular heater installed in the fridge&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Then the fridge and heater are plugged into the temperature controller. Some
advice I’d seen on the Amazon listing for the controller was to set the
thresholds to 0.5º for the fridge and 1.0º for the heater. The thermal mass of
the fermenting bucket will slow down any temperature changes, but this should
stop the fridge cycling on and off too often and shortening the life of it.&lt;/p&gt;

&lt;p&gt;Finally, I built a platform out of some bits of old wood. Cut wide enough and
then the edges thinned to slot into the place where the glass originally was,
the fermenter sits on top of these.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/brew-fridge-with-fermentation-bucket.jpeg&quot; alt=&quot;A modified fridge, showing the fermentation bucket fitting well&quot; max-width=&quot;250px&quot; /&gt;
  &lt;figcaption&gt;The fridge with everything installed and setup&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Overall, the project (including specific tools) came to £236.89, broken down
into:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Item&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Cost&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;&lt;a href=&quot;https://www.currys.co.uk/gbuk/household-appliances/refrigeration/fridges/essentials-cul55w20-undercounter-fridge-white-10205941-pdt.html&quot;&gt;Currys Essentials CUL55W20 Undercounter Fridge&lt;/a&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;£139.99&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;&lt;a href=&quot;https://www.amazon.co.uk/dp/B018K82UQU&quot;&gt;Inkbird ITC-308 plug-in temperature controller&lt;/a&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;£35.98&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;&lt;a href=&quot;https://www.amazon.co.uk/dp/B00L41ZVBW&quot;&gt;60W 1’ Tubular Heater&lt;/a&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;£20.99&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;&lt;a href=&quot;https://www.screwfix.com/p/time-3093y-white-3-core-0-75mm-flexible-cable-25m-drum/177jy?_requestid=582403&quot;&gt;Reel of 0.75mm² flex (you really only need about 2M!)&lt;/a&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;£13.49&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;&lt;a href=&quot;https://www.screwfix.com/p/diall-13a-fused-plug-white/5751h?_requestid=587427&quot;&gt;13A Plug&lt;/a&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;£0.93&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;&lt;a href=&quot;https://www.screwfix.com/p/3a-fuse-pack-of-10/94488?_requestid=587599&quot;&gt;Pack of 3A fuses&lt;/a&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;£1.90&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;&lt;a href=&quot;https://www.amazon.co.uk/dp/B089QM2W29&quot;&gt;Step Drill Set&lt;/a&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;£11.99&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;&lt;a href=&quot;https://www.rapidonline.com/elkay-1851e1000s0301-aqua-safe-screwed-waterproof-3-pole-male-housing-socket-23-1494&quot;&gt;Elkay 3 Pole Waterproof Socket&lt;/a&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;£5.52&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;&lt;a href=&quot;https://www.rapidonline.com/elkay-1850a1011p0301-aqua-safe-in-line-waterproof-3-pole-female-housing-plug-23-1480&quot;&gt;Elkay 3 Pole Waterpoof Plug&lt;/a&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;£6.18&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;future-expansion&quot;&gt;Future Expansion&lt;/h2&gt;

&lt;p&gt;For the first brew, I just taped the temperature probe to the outside of the
fermenter. I wasn’t brewing a beer which is very temperature sensitive (a
bitter close to Fuller’s ESB) and so I didn’t need to worry too much about the
extra degrees given off by the fermenting process.&lt;/p&gt;

&lt;p&gt;But for the second, I installed a thermowell — a device which allows the probe
to be isolated from — but still inside — the bucket. &lt;a href=&quot;https://www.themaltmiller.co.uk/product/thermowell-stainless-100mm-weldless/&quot;&gt;I got this one from The
Malt Miller&lt;/a&gt;, which whilst a bit pricier (at £18) did actually have
everything required in one purchase and so I didn’t need to hunting around for
washers or the right nut to fit.&lt;/p&gt;

&lt;p&gt;For now, this is the last of the modifications. It works great as it is and if
I wanted to do multi-day temperature changes that’s absolutely possible. In the
future, it might be interesting to add a BrewPi Spark but this is definitely
Good Enough™.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks to &lt;a href=&quot;https://interroban.gg&quot;&gt;Luke Mitchell&lt;/a&gt; for looking at a draft&lt;/em&gt;&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #24</title>
        <link href="https://nickcharlton.net/posts/week-notes-24.html" />
        <id>https://nickcharlton.net/posts/week-notes-24.html</id>
        <published>Mon, 25 Jan 2021 00:00:00 +0000</published>
        <updated>Mon, 25 Jan 2021 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;I’ve started a new habit this year of tidying up my desk before I leave at
the end of a Friday, now when I return on a Monday morning it’s nice and tidy
and ready to go, it’s very nice!&lt;/li&gt;
  &lt;li&gt;I finished &lt;a href=&quot;https://uk.bookshop.org/books/how-to-do-nothing-resisting-the-attention-economy/9781612197494&quot;&gt;&lt;em&gt;How To Do Nothing&lt;/em&gt;&lt;/a&gt; last week. It’s an excellent read and I’d
definitely recommend it, but it is one of those books you might just have to
read at least twice,&lt;/li&gt;
  &lt;li&gt;Then moved onto &lt;a href=&quot;https://uk.bookshop.org/books/gotta-get-theroux-this-my-life-and-strange-times-in-television/9781509880393&quot;&gt;Louis Theroux’s &lt;em&gt;Gotta Get Theroux This&lt;/em&gt;&lt;/a&gt;, plus the &lt;a href=&quot;https://uk.bookshop.org/books/the-99-invisible-city-a-field-guide-to-the-hidden-world-of-everyday-design/9781529355277&quot;&gt;&lt;em&gt;99%
Invisible City&lt;/em&gt;&lt;/a&gt;, both of which have been good,&lt;/li&gt;
  &lt;li&gt;I bottled the first beer I mentioned previously yesterday, so far spot on on
the expected alcohol percentage so I’m excited to try this in two weeks’
time,&lt;/li&gt;
  &lt;li&gt;Otherwise, lots of writing (which takes ages) in the background, plus a bit
of a site redesign I’ve been working on for a few months.&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>A weekend with a Flir ONE</title>
        <link href="https://nickcharlton.net/posts/a-weekend-with-a-flir-one.html" />
        <id>https://nickcharlton.net/posts/a-weekend-with-a-flir-one.html</id>
        <published>Fri, 15 Jan 2021 00:00:00 +0000</published>
        <updated>Fri, 15 Jan 2021 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I spent a couple of weekends back playing around with a &lt;a href=&quot;https://www.flir.co.uk/flir-one/&quot;&gt;Flir ONE&lt;/a&gt;, a
thermal camera which attaches to a phone. I’d been working on a project which
involved some gnarly drilling and was concerned I’d hit a pipe — it turned out
as a good excuse to give one a go.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/flir-one-device.jpeg&quot; alt=&quot;The Flir ONE device itself&quot; max-width=&quot;250px&quot; /&gt;
  &lt;figcaption&gt;The Flir ONE device itself&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;It’s a really neat device which worked well for my needs but is let down by
some fairly average software. The model I used was a few years old (the newer
ones have a much higher resolution) but this wasn’t a significant problem for
my intended use-case. As an example of the things I found: the device would
often disconnect whilst in use, sometimes during filming something which
required stopping, resetting and starting again. The view would “flash out”
completely; I wondered if this was a regular recalibration, but I couldn’t work
out a consistent behaviour for it. Finally, the software just didn’t really
follow iOS design affordances: lots of bits of UI which had no feedback or
touch controls which were far too small.&lt;/p&gt;

&lt;p&gt;Whilst I had it, I ended up taking a bunch of photos and some short videos of
various things:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/flir-one-houses.jpeg&quot; alt=&quot;A thermal image showing the houses on my street, highlighting how windows
    leak so much heat&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;A row of houses, showing where windows leak so much heat&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/flir-one-mug.jpeg&quot; alt=&quot;A mug of tea, showing where the liquid is hot and the mug itself is
    warmiing up&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;A mug of tea&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/flir-one-selfie.jpeg&quot; alt=&quot;A thermal image showing my face&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;A selfie&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
  &lt;video controls=&quot;&quot; max-width=&quot;500px&quot;&gt;
    &lt;source src=&quot;/resources/images/flir-one-3d-printer.mov&quot; type=&quot;video/mp4&quot; /&gt;
  &lt;/video&gt;
  &lt;figcaption&gt;My 3D printer bed heating up&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I rented it for a weekend from someone locally using &lt;a href=&quot;https://fatllama.com/r/nick-e7156&quot;&gt;Fat Llama&lt;/a&gt; which
worked out really well. I picked it up on Saturday morning and dropped it off
on the Monday for £30. It was great fun to have for a weekend, but with some of
the annoyances I don’t think I’d buy one.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #23</title>
        <link href="https://nickcharlton.net/posts/week-notes-23.html" />
        <id>https://nickcharlton.net/posts/week-notes-23.html</id>
        <published>Wed, 13 Jan 2021 00:00:00 +0000</published>
        <updated>Wed, 13 Jan 2021 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;First Week Notes of 2021!&lt;/li&gt;
  &lt;li&gt;It was a busy old time at the end of the year, where I both did nothing quite
worth writing up about and a lot of things I couldn’t,&lt;/li&gt;
  &lt;li&gt;The big news is that now I’m fully remote and won’t be going back to the
office when the pandemic restrictions lift (maybe for some Friday’s if others
do too, but not at all like I once did); this is both exciting and scary as
it opens up many options which previously weren’t possible,&lt;/li&gt;
  &lt;li&gt;On the book front, I just finished &lt;a href=&quot;https://uk.bookshop.org/books/english-pastoral-an-inheritance-the-sunday-times-bestseller-from-the-author-of-the-shepherd-s-life/9780141982571&quot;&gt;&lt;em&gt;English Pastoral: An Inheritance&lt;/em&gt;&lt;/a&gt;,
James Rebanks’ new book about inheriting his family’s farm and being a farmer
in the modern world. I enjoyed it a lot. In a few ways it pairs well with
George Monbiot’s &lt;a href=&quot;https://uk.bookshop.org/books/feral-rewilding-the-land-sea-and-human-life/9780141975580&quot;&gt;&lt;em&gt;Feral: Rewilding the Land, Sea and Human Life&lt;/em&gt;&lt;/a&gt; that I
read last year and provided a more nuanced view on what the countryside is
for, how we produce food and what it should be (specifically, I’m a big fan
of the idea of rewilding ecosystems but the details are quite nuanced; for
example, we’ve been doing organised farming for thousands of years and so the
natural world has evolved around us and so I don’t think it’s reasonable to
expect to return to vast amounts of temperate rainforest),&lt;/li&gt;
  &lt;li&gt;I’m now reading &lt;a href=&quot;https://uk.bookshop.org/books/how-to-do-nothing-resisting-the-attention-economy/9781612197494&quot;&gt;&lt;em&gt;How To Do Nothing: Resisting the Attention Economy&lt;/em&gt;&lt;/a&gt;
which is excellent. It’s the book that &lt;a href=&quot;https://medium.com/@the_jennitaur/how-to-do-nothing-57e100f59bbb&quot;&gt;formed this lovely talk from
2017&lt;/a&gt;, and (not quite what I was expecting) has similar themes from the
prior two books,&lt;/li&gt;
  &lt;li&gt;Before Christmas, I also read &lt;a href=&quot;https://uk.bookshop.org/books/what-got-you-here-won-t-get-you-there-how-successful-people-become-even-more-successful/9781846681370&quot;&gt;&lt;em&gt;What Got You Here Won’t Get You There&lt;/em&gt;&lt;/a&gt;,
&lt;a href=&quot;https://github.com/readme/alex-ellis&quot;&gt;which came by recommendation from Alex Ellis&lt;/a&gt;. It’s about habits that
might have been fine so far, but are now holding you back (like being a jerk
to people without realising it, or shutting down conversations when you don’t
mean to). I’d recommend it,&lt;/li&gt;
  &lt;li&gt;Outside of all of the cooking I did (I stayed in London, for the first time),
I built a Brew Fridge and brewed my first beer in couple of years. I’d
started brewing beer about four years ago, but in the old Victorian houses
I’ve been in since it’s been very difficult to have the right place to
ferment (over a period of two weeks and getting a stable temperature). So I
built something to ferment in. Longer post to follow, but it was much easier
to do than I expected!&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #22</title>
        <link href="https://nickcharlton.net/posts/week-notes-22.html" />
        <id>https://nickcharlton.net/posts/week-notes-22.html</id>
        <published>Mon, 02 Nov 2020 00:00:00 +0000</published>
        <updated>Mon, 02 Nov 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;It’s been the end of the first part of &lt;em&gt;Review Season&lt;/em&gt; this week, a time
where I spend an unreasonable amount of time staring blankly at a text
editor trying to come up with anything to say. It’s that perfect
combination of dreadfully difficult but also concerningly meaningful which
leads me to deep writers block. &lt;em&gt;vvvvroooommmmmmm&lt;/em&gt; the deadline goes, as
it flies by …and then suddenly words happen and manic typing begins. Except
this time, with one illusive answer to the question of
&lt;em&gt;what can x improve on?&lt;/em&gt; to leave me stuck once more,&lt;/li&gt;
  &lt;li&gt;Because just sitting at a blinking cursor never really helps once you’ve
been doing it for several hours, I tried to beat the rain by cleaning my
two bikes on Friday afternoon in preparation for servicing them. I got most
of my way through until it started raining right towards the end and then
hastily hurrying everything in after having been working under torchlight
for about half a hour. But now, a new chain, new brakes and two shiny
looking bikes all ready to go,&lt;/li&gt;
  &lt;li&gt;As &lt;a href=&quot;https://www.bbc.co.uk/news/uk-54763956&quot;&gt;another lockdown is due to start on Thursday&lt;/a&gt;, it was time to pick
up &lt;a href=&quot;https://www.brooksrunning.com/en_gb/ravenna-11-mens-road-running-shoe/110330.html&quot;&gt;new running shoes&lt;/a&gt; which, hopefully, will be the end of the bad
series of injuries I’ve had this year,&lt;/li&gt;
  &lt;li&gt;Otherwise, that’s been just about it. Much of this week has been working
down long issue lists and trying to review more Open Source PRs, slow but
gradual progress!&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #21</title>
        <link href="https://nickcharlton.net/posts/week-notes-21.html" />
        <id>https://nickcharlton.net/posts/week-notes-21.html</id>
        <published>Mon, 26 Oct 2020 00:00:00 +0000</published>
        <updated>Mon, 26 Oct 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;Back to work last week and it was a good one: started off with helping out
with a database migration (&lt;a href=&quot;https://www.digitalocean.com/products/managed-databases/&quot;&gt;DigitalOcean’s managed database solution&lt;/a&gt; is
pretty nice!) and then a new project starting with the second phase of a
project that’s just Rails. It’s good to be back in the land of Rails, a
place where I can comfortably test-drive new features in just a few hours
and really focus about the product we’re delivering rather than the minutiae
of the code,&lt;/li&gt;
  &lt;li&gt;I somehow ended up leaving a year (to the month!) in getting back to
reading &lt;a href=&quot;https://www.poodr.com&quot;&gt;&lt;em&gt;Practical Object Oriented Design in Ruby&lt;/em&gt;&lt;/a&gt;, but I finished the
final chapter this week. It’s good and well recommended,&lt;/li&gt;
  &lt;li&gt;After just short of two weeks using &lt;a href=&quot;https://rectangleapp.com&quot;&gt;&lt;em&gt;Rectangle&lt;/em&gt;&lt;/a&gt;, I’ve gotten used to
it now and it’s working great. I generally use four quadrants: Slack,
Drafts, Mail (although I only open this once in the morning), then often
Chrome (which I use for work) on one half of the screen. Occasionally I’ve
been using a two-thirds/one-third split with Chrome and Drafts for calls,
as Jira can’t quite fit otherwise, my terminal session always fullscreen,&lt;/li&gt;
  &lt;li&gt;This weekend saw me tear down the greenhouse that I’ve had setup for the
last two years; it was just a plastic tent and in the sun they don’t last
forever. I ended up picking the last two aubergines and the last cucumber
of the season,&lt;/li&gt;
  &lt;li&gt;Then frame three prints that I’ve been meaning to do for years: one a
Guinness poster which came in a rotting frame and I finally replaced, an
early-run of &lt;a href=&quot;https://cameronmoll.bigcartel.com/product/brooklyn-bridge-24-x16-letterpress-poster-corrected&quot;&gt;Cameron Moll’s Brooklyn Bridge letterpress print&lt;/a&gt; and
finally a print of the design proof of the BR logo which came out of
&lt;a href=&quot;https://www.kickstarter.com/projects/1863728218/british-rail-corporate-identity-manual/description&quot;&gt;Wallace Henning’s work on the Corporate Identity Manual&lt;/a&gt;. It’s only
taken me about 8 years to start doing this!&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #20</title>
        <link href="https://nickcharlton.net/posts/week-notes-20.html" />
        <id>https://nickcharlton.net/posts/week-notes-20.html</id>
        <published>Sun, 18 Oct 2020 00:00:00 +0000</published>
        <updated>Sun, 18 Oct 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;A second week of doing my own stuff; much of the same but a relaxing way to
spend a week as I get around to some things I’ve wanted to do for months,&lt;/li&gt;
  &lt;li&gt;I started off by spending a day doing some workflow improvements:
    &lt;ul&gt;
      &lt;li&gt;I switched from &lt;a href=&quot;https://magnet.crowdcafe.com&quot;&gt;Magnet&lt;/a&gt; to &lt;a href=&quot;https://rectangleapp.com&quot;&gt;Rectangle&lt;/a&gt; for macOS window management,&lt;/li&gt;
      &lt;li&gt;Started the move to &lt;a href=&quot;https://sw.kovidgoyal.net/kitty/index.html#&quot;&gt;kitty&lt;/a&gt;, with a hurdle around first removing
&lt;a href=&quot;http://chriskempson.com/projects/base16/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base16&lt;/code&gt;&lt;/a&gt; which I’d previously been using to handle colours. It worked
really well, but now I’ve learned more about ANSI colours I’ve found I can do
the bits I want manually …but I didn’t quite start on this yet,&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;More &lt;a href=&quot;https://tailwindcss.com&quot;&gt;Tailwind&lt;/a&gt; this week, finishing up the menu for the side project
I’ve been working on and then doing a little “accounts” view so there’s
something to see once you’re logged in,&lt;/li&gt;
  &lt;li&gt;In implementing this accounts view (it’s a list of bank accounts a user
has), I used this as the opportunity to try out &lt;a href=&quot;https://github.com/github/view_component&quot;&gt;ViewComponent&lt;/a&gt;. I
found that it fit really nicely between being able to test drive a view and
not write a heavyweight feature/system test with the added advantage of
knowing that reusing components would be much easier in future,&lt;/li&gt;
  &lt;li&gt;I switched gears at the end of the week to pick up some maintenance of a
project I started about four years ago for notifying you on library
releases and I never launched. It’s kinda not necessary now as GitHub does
have a native feature for it, but I’d wanted something to build on to track
a few other things so I picked it back up. I got it working again (I’d
broken the background jobs at some point), &lt;a href=&quot;https://github.com/thoughtbot/neat&quot;&gt;replaced Neat with CSS Grid&lt;/a&gt;
and then came up a with a short list of things to do to actually launch it,&lt;/li&gt;
  &lt;li&gt;In my continuing discovery of &lt;em&gt;Linux on the Desktop&lt;/em&gt; (with a barebones
Debian and i3), I learned about &lt;a href=&quot;https://wiki.debian.org/SecureBoot&quot;&gt;UEFI SecureBoot&lt;/a&gt; and signed a kernel
module, plus made both audio and the backlight keys work]. As I write this,
I still can’t quite copy and paste how I’d like,&lt;/li&gt;
  &lt;li&gt;I always enjoy &lt;a href=&quot;https://themargins.substack.com&quot;&gt;&lt;em&gt;The Margins&lt;/em&gt;&lt;/a&gt;, a newsletter by Can Durak and Ranjan
Roy, &lt;a href=&quot;https://themargins.substack.com/p/thinking-and-feeling-how-to-read&quot;&gt;but this weeks’ on how to consume news&lt;/a&gt; was particularly good,&lt;/li&gt;
  &lt;li&gt;I’ve also been enjoying reading the &lt;a href=&quot;https://tailscale.com&quot;&gt;Tailscale&lt;/a&gt; blog and
&lt;a href=&quot;https://tailscale.com/blog/how-nat-traversal-works/&quot;&gt;this on how they traverse NAT was really interesting&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #19</title>
        <link href="https://nickcharlton.net/posts/week-notes-19.html" />
        <id>https://nickcharlton.net/posts/week-notes-19.html</id>
        <published>Sun, 11 Oct 2020 00:00:00 +0000</published>
        <updated>Sun, 11 Oct 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;Last week was the end of a — long for me — nine-month project. It was not
the easiest nor was it working in a particularly exciting to me technology
(React and later React Native) but I learned a lot. And spent a lot of time
in Zoom calls,&lt;/li&gt;
  &lt;li&gt;Unfortunately, the client has been badly affected by COVID-19 so the project
ended up finishing much earlier than we’d hoped for (it’s nice to actually
finish the work you start!) and a little abruptly: the last week was
wrapping up everything we could, leaving notes and fixing a load of small
annoyances I’d wanted to for months,&lt;/li&gt;
  &lt;li&gt;But that did open up an opportunity to take some time off, so this week
(and also next) I’ve been working on my own stuff. A mix of fixing things
which I’d wanted to, but were much more involved than I could find the time
for plus trying out a few new things,&lt;/li&gt;
  &lt;li&gt;I started off by profiling my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zsh&lt;/code&gt; configuration; it’d gotten horribly
slow. I started off with 1.33 minutes (!), and got it down to 0.08 seconds
by switching &lt;a href=&quot;https://github.com/nickcharlton/dotfiles/commit/50101cf93708dfba334465e44e4b7f69dfcfdcaf&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nvm&lt;/code&gt; (the main culprit)&lt;/a&gt; and &lt;a href=&quot;https://github.com/nickcharlton/dotfiles/commit/b7e1e35302d6307892d7cee7b19afc0524b47672&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jabba&lt;/code&gt; to lazy-load&lt;/a&gt;.
It was a lot easier to do than I expected, I started off by &lt;a href=&quot;https://stevenvanbael.com/profiling-zsh-startup&quot;&gt;profiling as
explained in this article&lt;/a&gt; and then &lt;a href=&quot;https://peterlyons.com/problog/2018/01/zsh-lazy-loading/&quot;&gt;trying out a few lazy&lt;/a&gt; &lt;a href=&quot;https://frederic-hemberger.de/notes/shell/speed-up-initial-zsh-startup-with-lazy-loading/&quot;&gt;loading
approaches&lt;/a&gt;,&lt;/li&gt;
  &lt;li&gt;I spent Monday and Tuesday on &lt;a href=&quot;https://github.com/thoughtbot/administrate&quot;&gt;Administrate&lt;/a&gt; and closed 74 issues and
PRs! I enjoy working on this project (when I get the time), but it’s a lot
of work to keep on top of and I usually find I drop behind for a few months
at a time and then the backlog becomes somewhat overwhelming. My goal this
year has been get below 50 open issues and 30 open PRs and as I write this
we’re on 65 and 31 respectively, so it looks like I might actually make it,&lt;/li&gt;
  &lt;li&gt;For a long time, I’ve been working on a tool for pulling my bank data into
&lt;a href=&quot;https://www.youneedabudget.com&quot;&gt;YNAB&lt;/a&gt; (an application which has been fairly transformative for me),
it’s just for me so it works out as a good platform for experimenting on.
It was feature complete about a year ago until the service I was using to
pull UK bank data was shutdown and since I’ve been building out a UI so
that you could log in to it. This is is needed because the (relatively new)
Open Banking APIs in the UK require the end user to click through to
confirm — previously it was a Rails app which just had Sidekiq to run some
background jobs for syncing,&lt;/li&gt;
  &lt;li&gt;Anyway, I’ve been working with &lt;a href=&quot;https://tailwindcss.com&quot;&gt;Tailwind&lt;/a&gt; on and off for a while plus
using &lt;a href=&quot;https://tailwindui.com&quot;&gt;Tailwind UI&lt;/a&gt;, and this week I added &lt;a href=&quot;https://stimulusjs.org/&quot;&gt;Stimulus&lt;/a&gt; to do some
interaction. After spending most of the year working on a huge React app,
Stimulus is very satisfying: much closer to the way the browser actually
works so you’re not fighting with someone else’s interpretation of how it
should be and so little unexpected magic (which I always felt with
&lt;a href=&quot;https://redux.js.org&quot;&gt;Redux&lt;/a&gt;) &lt;a href=&quot;https://stimulusjs.org/reference/controllers&quot;&gt;and the docs are relatively tiny&lt;/a&gt;,&lt;/li&gt;
  &lt;li&gt;Like many people, I have a bit of an aversion to the &lt;em&gt;huge&lt;/em&gt; amount of
classes you end up putting in a “component” and my first instinct was to
extract these out (in the original version of this project, that’s exactly
what I did). But it took hours of working out naming and revisiting CSS
conventions. I’m building a menu that I’m very likely to never touch agin,
what’s the point? I think the individual component solution is going to be
&lt;a href=&quot;https://github.com/github/view_component&quot;&gt;ViewComponent&lt;/a&gt;, which I’m going to try out soon,&lt;/li&gt;
  &lt;li&gt;Finally, I picked up a &lt;a href=&quot;https://www.thinkwiki.org/wiki/Category:T470s&quot;&gt;Thinkpad T470s&lt;/a&gt;. I have — for about the last
three years or so — been thinking about trying to run Debian and the
&lt;a href=&quot;https://i3wm.org&quot;&gt;i3 tiling window manager&lt;/a&gt; and see how I go with it. It’s been good so
far, but my expectations are to use it for messing about with, rather than
anything more; can you imagine having to piss about with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xrandr&lt;/code&gt; to get a
presentation to work in a client meeting? nightmare-ish.&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #18</title>
        <link href="https://nickcharlton.net/posts/week-notes-18.html" />
        <id>https://nickcharlton.net/posts/week-notes-18.html</id>
        <published>Tue, 15 Sep 2020 00:00:00 +0000</published>
        <updated>Tue, 15 Sep 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;Unfortunately, as I write this, I have another — hopefully not so long
lasting — running-related injury as I’ve pulled a muscle or perhaps sprained
my right lower leg/ankle. Queue a day of hobbling around the house and
cursing having stairs,&lt;/li&gt;
  &lt;li&gt;My recent injuries have been caused by a combination of things I’ve been
gradually understanding over the past year: spending the last decade plus
generally sitting has caused a hip imbalance (where the muscles at the front
are much weaker), which leads to bad feet placement (pronation) which in turn
can lead to various injuries (if you don’t have the right sort of support),&lt;/li&gt;
  &lt;li&gt;Ironically, standing with the new desk over the past month has likely
contributed too!&lt;/li&gt;
  &lt;li&gt;tl;dr: I need new shoes and to work on improving my gait and strength again.
&lt;a href=&quot;https://www.youtube.com/watch?v=cTZOqbR_3hU&quot;&gt;This video has some really good background on it&lt;/a&gt;,&lt;/li&gt;
  &lt;li&gt;In other news, I moved my &lt;em&gt;Weekly Review&lt;/em&gt; (this is something I’ve been meaning
for months to write up) forward from Monday to Sunday. I find there’s enough
going on that I just can’t get a good deep bit of reflection on Monday and so
I’ve been skipping through far too quickly. I’m trying this now on a Sunday
afternoon to see how this goes,&lt;/li&gt;
  &lt;li&gt;I’ve been continuing to &lt;a href=&quot;https://nickcharlton.net/posts/week-notes-17.html&quot;&gt;setup the Synology I mentioned last week&lt;/a&gt;, mostly
through moving data off ancient, disparate hard drives (including 2TB of old
backups) into a folder structure I spent far too much time thinking through,&lt;/li&gt;
  &lt;li&gt;One of the motivations behind getting the Synology has been to decommission
the &lt;a href=&quot;https://www.hetzner.com&quot;&gt;Hetzner&lt;/a&gt; setup (&lt;a href=&quot;https://nickcharlton.net/posts/configuring-esxi-6-on-hetzner.html&quot;&gt;which I’ve written about previously&lt;/a&gt;) I have and
set something else up for experiments; this week a &lt;a href=&quot;https://www.ui.com/edgemax/edgerouter-x/&quot;&gt;Ubiquiti EdgeRouter X&lt;/a&gt;
arrived which will be part of the network for that. The plan is broadly: move
any experiments there locally and setup a tunnel to make it accessible.&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #17</title>
        <link href="https://nickcharlton.net/posts/week-notes-17.html" />
        <id>https://nickcharlton.net/posts/week-notes-17.html</id>
        <published>Tue, 08 Sep 2020 00:00:00 +0000</published>
        <updated>Tue, 08 Sep 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;I’m now three weeks into having a standing desk and I’ve been doing a 50/50
split of sitting and standing ever since. The most interesting thing so
far — and also one of the things I was very excited about — has been that
I’m no longer so tired after work. I’ve been finding that standing keeps me
going much longer (or, maybe, consistently?) which has always been something
I’ve struggled with,&lt;/li&gt;
  &lt;li&gt;Another thing has been that I think I’ve split the past feeling of tiredness
and hunger at the end of the day: I can stop working, go and do something
else and then have dinner a bit later instead of needing to go eat right at
the point I finish. It’s much nicer.&lt;/li&gt;
  &lt;li&gt;But I just had a week off where I did as little as possible apart from
sorting out this and that, a spot of gardening and visiting my Mum for the
first time eight months. It was particularly noticeable after a few
stressful weeks!&lt;/li&gt;
  &lt;li&gt;In other news, I bought a NAS (network attached storage device). On the plan
it was an investment, I got a &lt;a href=&quot;https://www.synology.com/en-uk/products/DS1520+&quot;&gt;Synology DS1520+&lt;/a&gt; which will fit five
drives (it is a little excessive!) plus the first 4TB HDD. I’ll be getting
more drives over the next six months. More to write up here on my plans for
it, but all the obvious stuff: backups, making some media accessible and
&lt;a href=&quot;https://unifi-network.ui.com&quot;&gt;a few&lt;/a&gt; &lt;a href=&quot;https://www.archiveteam.org/index.php?title=ArchiveTeam_Warrior&quot;&gt;utility VMs&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #16</title>
        <link href="https://nickcharlton.net/posts/week-notes-16.html" />
        <id>https://nickcharlton.net/posts/week-notes-16.html</id>
        <published>Mon, 17 Aug 2020 00:00:00 +0000</published>
        <updated>Mon, 17 Aug 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;I made an &lt;a href=&quot;https://github.com/nickcharlton/pronto-standardrb-actions-example&quot;&gt;example of using Pronto with standardrb that’ll run on GitHub
Actions&lt;/a&gt;. It worked out to be one of those things which (mostly) just
slotted together and worked, which was nice,&lt;/li&gt;
  &lt;li&gt;A &lt;a href=&quot;https://www.komoot.com/tour/236272759&quot;&gt;warm cycle around London&lt;/a&gt; last week, which mostly reminded me how poorly
thought through much of the cycle infrastructure is in certain boroughs:
coming off a road into a “dual use” pedestrian/cycling area in a busy shopping
area is a dreadful bit of street design,&lt;/li&gt;
  &lt;li&gt;Over the past few weeks I’ve been struggling with a mix of back and
(eventually) shoulder pain which is part stress but also part my desk setup,&lt;/li&gt;
  &lt;li&gt;Step 1 was to replace the &lt;a href=&quot;https://www.ikea.com/gb/en/p/markus-office-chair-vissle-dark-grey-30261152/&quot;&gt;old IKEA chair I had&lt;/a&gt; (this is a good chair;
just not for my needs) with one from the office: I now have an &lt;a href=&quot;https://www.hermanmiller.com/en_gb/products/seating/office-chairs/aeron-chairs/&quot;&gt;Aeron&lt;/a&gt;
and this made a huge difference,&lt;/li&gt;
  &lt;li&gt;Step 2 was to get a standing desk — something I’ve wanted for years — and so
&lt;a href=&quot;https://www.ikea.com/gb/en/p/bekant-desk-sit-stand-oak-veneer-white-s09061188/&quot;&gt;I now have one of those&lt;/a&gt; (there’s a picture below),&lt;/li&gt;
  &lt;li&gt;After a long day, it does seem to have helped a lot. My big hope was to get
to the end of it and not feel so tired and so far, that’s worked out!&lt;/li&gt;
&lt;/ul&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/week-notes-16-standing-desk.jpg&quot; alt=&quot;Standing desk with monitor setup&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Standing desk with monitor setup&lt;/figcaption&gt;
&lt;/figure&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #15</title>
        <link href="https://nickcharlton.net/posts/week-notes-15.html" />
        <id>https://nickcharlton.net/posts/week-notes-15.html</id>
        <published>Wed, 29 Jul 2020 00:00:00 +0000</published>
        <updated>Wed, 29 Jul 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;I haven’t written these for a few weeks as pretty much every week has become
the same thing and then you realise it’s been quite a long time…&lt;/li&gt;
  &lt;li&gt;A few weekends ago, I had my first actual pint in a pub and it was a strange
experience and not quite the full experience I’d hoped for. But it’s a start!&lt;/li&gt;
  &lt;li&gt;I’ve been out doing long-cycles again in the much nicer weather and
&lt;a href=&quot;https://www.wtb.com/collections/gravel-cx/products/resolute&quot;&gt;I have new gravel tyres&lt;/a&gt; which should be much better on that sort of
terrain that I’ve been getting into more so now,&lt;/li&gt;
  &lt;li&gt;I’ve also picked up &lt;a href=&quot;https://www.apidura.com/shop/expedition-frame-pack/&quot;&gt;a frame bag (something I wanted already for bike
packing)&lt;/a&gt;, but has now become a permanent fixture on the bike holding a
spare tube, pump, battery, tools and some snacks,&lt;/li&gt;
  &lt;li&gt;I made a &lt;a href=&quot;https://getdrafts.com/&quot;&gt;Drafts&lt;/a&gt; &lt;a href=&quot;https://actions.getdrafts.com/a/1ah&quot;&gt;action to extract a selection from one Draft into
another&lt;/a&gt;. I had a long list of 1-on-1 notes in one long draft, which was
starting to be unmanageable, so I’ve now split them into lots of smaller
drafts quite easily,&lt;/li&gt;
  &lt;li&gt;We started a new project with &lt;a href=&quot;https://makers.tech&quot;&gt;Makers Academy&lt;/a&gt;, having three people who
recently graduated work on &lt;a href=&quot;https://github.com/thoughtbot/administrate&quot;&gt;Administrate&lt;/a&gt; for a few months. It’s been
going well so far,&lt;/li&gt;
  &lt;li&gt;Finally, after being unable to for the past three-months, I’m able to run
again and that’s been such a significant one for me to miss.&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #14</title>
        <link href="https://nickcharlton.net/posts/week-notes-14.html" />
        <id>https://nickcharlton.net/posts/week-notes-14.html</id>
        <published>Tue, 26 May 2020 00:00:00 +0000</published>
        <updated>Tue, 26 May 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;h1 id=&quot;week-notes-14&quot;&gt;Week Notes #14&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;Since my last post, I published &lt;a href=&quot;https://nickcharlton.net/posts/setting-jenkins-credentials-with-groovy.html&quot;&gt;&lt;em&gt;Setting Jenkins Credentials with
Groovy&lt;/em&gt;&lt;/a&gt;. I’ve been using some of the extra time I’ve had from lockdown to
wrap up some things I started years ago and this is part of the same thread.
There’s a few more posts to come related to this before I get onto something
a bit more interesting,&lt;/li&gt;
  &lt;li&gt;But now that the weather has gotten much hotter, I’ve been spending more time
outside. I grew a bunch of plants last year and I’m doing the same this year.
To start off with, I’ve been trying to make everything a bit easier than it
was last year (I had too many plants and so watering took ages!),&lt;/li&gt;
  &lt;li&gt;One of the goals this year was to make the lawn look better and so last
weekend, I sowed grass seed. To avoid having all of the seed eaten, I built
twelve stakes to make a platform for mounting netting over. I also did a
temporary extension of the garden tap to make access to water much less of a
pain,&lt;/li&gt;
  &lt;li&gt;This weekend, I started building some window boxes &amp;amp; planters out of some
scrap wood I’d collected over the past few years. There’s a photo below.
There’s something really nice about working with scrap wood; this is all from
old shipping pallets which shouldn’t be burned (they contain chemicals to
protect the wood) so it’s really cool to see them get more life.&lt;/li&gt;
&lt;/ul&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/week-notes-14-pallet-planters.jpg&quot; alt=&quot;Planters and Window Boxes from old shipping pallets&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Planters and Window Boxes from old shipping pallets&lt;/figcaption&gt;
&lt;/figure&gt;

</summary>
    </entry>
    
    <entry>
        <title>Setting Jenkins Credentials with Groovy</title>
        <link href="https://nickcharlton.net/posts/setting-jenkins-credentials-with-groovy.html" />
        <id>https://nickcharlton.net/posts/setting-jenkins-credentials-with-groovy.html</id>
        <published>Fri, 08 May 2020 00:00:00 +0000</published>
        <updated>Fri, 08 May 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I’ve been building up a nice pattern for bootstrapping Jenkins’ secrets
through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init.groovy.d&lt;/code&gt;, storing the secrets themselves inside configuration
management. So far, this has been the simplest way to get a working
configuration without additional moving parts beyond Jenkins and a
configuration management tool.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://plugins.jenkins.io/credentials/&quot;&gt;Jenkins Credentials plugin&lt;/a&gt; supports a few different secret types:
“secret text” (which can be used as an environment variable), username &amp;amp;
password, files from the file system and a few others. We can handle SSH private
keys using the &lt;a href=&quot;https://plugins.jenkins.io/ssh-credentials&quot;&gt;SSH Credentials plugin&lt;/a&gt;. These can be made available
globally (i.e.: across multiple build nodes), or just on specific build nodes
but here we’re just going to treat them as global.&lt;/p&gt;

&lt;h2 id=&quot;as-initgroovyd-scripts&quot;&gt;As &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init.groovy.d&lt;/code&gt; scripts&lt;/h2&gt;

&lt;p&gt;I’ve used a few sources to understand how to do this in the past, especially
&lt;a href=&quot;https://gist.github.com/chrisvire/383a2c7b7cfb3f55df6a&quot;&gt;this Gist&lt;/a&gt;. But I wanted to do this incrementally and be cautious on which
dependencies were being imported. So, here’s how to implement each:&lt;/p&gt;

&lt;h3 id=&quot;secret-text&quot;&gt;Secret Text&lt;/h3&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/usr/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;groovy&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;jenkins.model.Jenkins&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.cloudbees.plugins.credentials.domains.Domain&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.cloudbees.plugins.credentials.CredentialsScope&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;hudson.util.Secret&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Jenkins&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;instance&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;domain&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Domain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getExtensionList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;com.cloudbees.plugins.credentials.SystemCredentialsProvider&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getStore&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;secretText&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringCredentialsImpl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;CredentialsScope&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;GLOBAL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;SECRET_NAME&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;SECRET_DESCRIPTION&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;Secret&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;fromString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SECRET_TEXT&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addCredentials&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;secretText&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;username--password&quot;&gt;Username &amp;amp; Password&lt;/h3&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/usr/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;groovy&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;jenkins.model.Jenkins&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.cloudbees.plugins.credentials.domains.Domain&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.cloudbees.plugins.credentials.CredentialsScope&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Jenkins&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;instance&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;domain&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Domain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getExtensionList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;com.cloudbees.plugins.credentials.SystemCredentialsProvider&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getStore&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;usernameAndPassword&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UsernamePasswordCredentialsImpl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;CredentialsScope&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;GLOBAL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;SECRET_NAME&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;SECRET_DESCRIPTION&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;USERNAME&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;PASSWORD&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addCredentials&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;usernameAndPassword&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/jenkinsci/credentials-plugin/blob/master/src/main/java/com/cloudbees/plugins/credentials/impl/UsernamePasswordCredentialsImpl.java&quot;&gt;The source for the implementation contains some additional options.&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;ssh-private-key&quot;&gt;SSH Private Key&lt;/h3&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/usr/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;groovy&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;jenkins.model.Jenkins&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.cloudbees.plugins.credentials.domains.Domain&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.cloudbees.plugins.credentials.CredentialsScope&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Jenkins&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;instance&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;domain&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Domain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getExtensionList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;com.cloudbees.plugins.credentials.SystemCredentialsProvider&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getStore&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;privateKey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BasicSSHUserPrivateKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;DirectEntryPrivateKeySource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&apos;
PRIVATE_KEY_TEXT
  &apos;&apos;&apos;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;sshKey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BasicSSHUserPrivateKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;CredentialsScope&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;GLOBAL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;SECRET_TEXT&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;PRIVATE_KEY_USERNAME&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;privateKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;PRIVATE_KEY_PASSPHRASE&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;SECRET_DESCRIPTION&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addCredentials&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sshKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This was more difficult to figure out than the others, &lt;a href=&quot;https://github.com/jenkinsci/ssh-credentials-plugin/blob/master/src/main/java/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKey.java#L108&quot;&gt;with the implementation
of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BasicSSHUserPrivateKey.java&lt;/code&gt; invaluable&lt;/a&gt;. The indentation of
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PRIVATE_KEY_TEXT&lt;/code&gt; is missing so that extraneous whitespace doesn’t cause
problems. I found that where Jenkins reads the key in itself, indentation is
not significant (like when connecting to build nodes) but where it’s reused
elsewhere (like the &lt;a href=&quot;https://github.com/jenkinsci/git-plugin&quot;&gt;Git plugin&lt;/a&gt;) would fail to connect to the repository.&lt;/p&gt;

&lt;p&gt;It supports multiple different types of SSH keys: entering directly (what we’re
doing here) but also reading off the disk. &lt;a href=&quot;http://groovy-lang.org/syntax.html#_triple_single_quoted_string&quot;&gt;We need to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;&apos;&apos;&lt;/code&gt; to have a
multi-line string&lt;/a&gt; as the private key will span a few lines. You’ll need
the &lt;a href=&quot;https://plugins.jenkins.io/ssh-credentials&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh-credentials&lt;/code&gt; plugin installed&lt;/a&gt;, too.&lt;/p&gt;

&lt;h2 id=&quot;configuring-with-ansible&quot;&gt;Configuring with Ansible&lt;/h2&gt;

&lt;p&gt;I’m using &lt;a href=&quot;https://docs.ansible.com/ansible/latest/index.html&quot;&gt;Ansible&lt;/a&gt; (with secrets stored in &lt;a href=&quot;https://docs.ansible.com/ansible/latest/user_guide/vault.html&quot;&gt;Ansible Vault&lt;/a&gt;), based
around &lt;a href=&quot;https://github.com/geerlingguy/ansible-role-jenkins&quot;&gt;Jeff Geerling’s Ansible role for Jenkins&lt;/a&gt;, but this pattern could
be replicated elsewhere.&lt;/p&gt;

&lt;p&gt;Ansible Vault arranges secrets by encrypting variables which are
accessible when playbooks are run. My original idea was to have a single
variable name which when written would reflect on the type you set, for example:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;jenkins_global_secrets&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;EXAMPLE_SECRET_TEXT&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;An example secret text value&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;a_very_important_secret&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;:secret_text&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But this caused quite a bit complexity when trying to work out where the logic
should be. To bridge the secrets from Ansible Vault to the provisioned machine,
they’re written out &lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/template_module.html#examples&quot;&gt;from a &lt;em&gt;template&lt;/em&gt; of the Groovy file&lt;/a&gt; and so splitting
some of the logic between the template stage (which Ansible does) and the
Groovy file (which is executed on runtime) felt misguided.&lt;/p&gt;

&lt;p&gt;A much nicer approach seemed to be to use many top level variables, and instead
you end up with this:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;jenkins_secret_text_credentials&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;EXAMPLE_SECRET_TEXT&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;An example secret text value&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;a_very_important_secret&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/chef-cookbooks/jenkins#jenkins_credentials&quot;&gt;Which is very similar to how the Jenkins Chef cookbook solves this
problem&lt;/a&gt;. From here, we can use this in a template like this
(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;configure_jenkins_credentials.groovy.j2&lt;/code&gt;):&lt;/p&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/usr/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;groovy&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;jenkins.model.Jenkins&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.cloudbees.plugins.credentials.domains.Domain&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.cloudbees.plugins.credentials.CredentialsScope&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;hudson.util.Secret&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Jenkins&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;instance&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;domain&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Domain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getExtensionList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;com.cloudbees.plugins.credentials.SystemCredentialsProvider&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getStore&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;secret&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jenkins_global_secrets&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;secretText&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringCredentialsImpl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;CredentialsScope&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;GLOBAL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;{{ secret[&apos;name&apos;] }}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;{{ secret[&apos;description&apos;] }}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;Secret&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;fromString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;{{ secret[&apos;text&apos;] }}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addCredentials&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;secretText&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endfor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In Ansible, this would then be written to the right place with something like
this:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Place the Jenkins Credentials Groovy script&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;configure_jenkins_credentials.groovy.j2&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;jenkins_home&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;}}/init.groovy.d/configure_jenkins_credentials.groovy&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With multiple top-level variables like this, the final result ends up being:&lt;/p&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/usr/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;groovy&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;jenkins.model.Jenkins&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.cloudbees.plugins.credentials.domains.Domain&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.cloudbees.plugins.credentials.CredentialsScope&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;hudson.util.Secret&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Jenkins&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;instance&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;domain&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Domain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getExtensionList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;com.cloudbees.plugins.credentials.SystemCredentialsProvider&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getStore&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;credential&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jenkins_secret_text_credentials&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;secretText&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringCredentialsImpl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;CredentialsScope&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;GLOBAL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;{{ credential[&apos;name&apos;] }}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;{{ credential[&apos;description&apos;] }}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;Secret&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;fromString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;{{ credential[&apos;text&apos;] }}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addCredentials&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;secretText&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endfor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;credential&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jenkins_ssh_credentials&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;privateKey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BasicSSHUserPrivateKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;DirectEntryPrivateKeySource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&apos;
{{ credential[&apos;private_key&apos;] }}
  &apos;&apos;&apos;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;sshKey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BasicSSHUserPrivateKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CredentialsScope&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;GLOBAL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                                    &lt;span class=&quot;s2&quot;&gt;&quot;{{ credential[&apos;name&apos;] }}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                                    &lt;span class=&quot;s2&quot;&gt;&quot;{{ credential[&apos;username&apos;] }}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                                    &lt;span class=&quot;n&quot;&gt;privateKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                                    &lt;span class=&quot;s2&quot;&gt;&quot;{{ credential[&apos;passphrase&apos;] }}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                                    &lt;span class=&quot;s2&quot;&gt;&quot;{{ credential[&apos;description&apos;] }}&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addCredentials&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sshKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endfor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;credential&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jenkins_username_password_credentials&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;usernameAndPassword&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UsernamePasswordCredentialsImpl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;CredentialsScope&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;GLOBAL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;{{ credential[&apos;name&apos;] }}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;{{ credential[&apos;description&apos;] }}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;{{ credential[&apos;username&apos;] }}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;{{ credential[&apos;password&apos;] }}&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addCredentials&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;usernameAndPassword&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endfor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…and then how the secrets would be structured:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;jenkins_secret_text_credentials&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;SECRET_TEXT&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;SECRET_DESCRIPTION&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;SECRET_TEXT&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;jenkins_ssh_credentials&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;SSH_KEY&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;PRIVATE_KEY_USERNAME&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;private_key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;-----BEGIN OPENSSH PRIVATE KEY-----&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;...&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;-----END OPENSSH PRIVATE KEY-----&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;passphrase&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;PRIVATE_KEY_PASSPHRASE&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;SECRET_DESCRIPTION&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;jenkins_username_password_credentials&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;SECRET_USERNAME&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;SECRET_DESCRIPTION&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;SECRET_USERNAME&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;SECRET_PASSWORD&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #13</title>
        <link href="https://nickcharlton.net/posts/week-notes-13.html" />
        <id>https://nickcharlton.net/posts/week-notes-13.html</id>
        <published>Tue, 05 May 2020 00:00:00 +0000</published>
        <updated>Tue, 05 May 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;I got a fun new toy this week: The &lt;a href=&quot;https://www.apple.com/uk/ipad-pro/&quot;&gt;new iPad Pro&lt;/a&gt;. I went for the bigger
screen this time (I’ve had a few over the years and developed a few iPad apps
for them) and also got the &lt;a href=&quot;https://www.apple.com/uk/shop/product/MXQT2B/A/magic-keyboard-for-ipad-pro-11-inch-2nd-generation-british-english&quot;&gt;new keyboard which has a trackpad built in&lt;/a&gt;.
The trackpad is curious, I still mostly tap the screen and don’t feel I’ve
gotten much out of it yet (the cursor changing dynamically is a cool UI
effect though). Having a good keyboard will change things more, I’m aiming
to write some of the workflow adjustments when I get more used to it,&lt;/li&gt;
  &lt;li&gt;Part of this has been a plan to try and use my laptop less often, which has
become more important during lockdown,&lt;/li&gt;
  &lt;li&gt;My friend &lt;a href=&quot;http://www.milesrjohnson.com&quot;&gt;Miles&lt;/a&gt; released &lt;a href=&quot;https://apps.apple.com/us/app/keep-touch/id1505817681&quot;&gt;Keep Touch&lt;/a&gt;, which prompts you on an
interval you set to check in with those you love. We paired a little on some
of the time calculations which was fun but also quite difficult to think
about,&lt;/li&gt;
  &lt;li&gt;Planted out some seeds this weekend, a mix of tomatoes, cucumbers, chillis
and peas. I did quite a lot last year which worked out to be far too much, so
slimmed it down this year,&lt;/li&gt;
  &lt;li&gt;I made a point of making quite a few notes on what was difficult or just
generally a pain to do. Watering was one of those, so I’m going to automate
some of it to reduce the workload,&lt;/li&gt;
  &lt;li&gt;Open source activity — as seen through my GitHub notifications — is peaky,
which I find continually curious. In the UK we had a long weekend over Easter
and it’s taken me until this week to get through it all. But as we start this
week it’s gotten all quiet. You’d expect activity of others to jump once mine
does, but this isn’t all of it. I think it’d be an interesting thing to
research.&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #12</title>
        <link href="https://nickcharlton.net/posts/week-notes-12.html" />
        <id>https://nickcharlton.net/posts/week-notes-12.html</id>
        <published>Tue, 28 Apr 2020 00:00:00 +0000</published>
        <updated>Tue, 28 Apr 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;Just after the last Week Notes post, &lt;a href=&quot;https://nickcharlton.net/posts/configuring-jenkins-email-ext-plugin-groovy.html&quot;&gt;I published a short one on configuring
Jenkins’ email-ext plugin&lt;/a&gt;. Jenkins, as much as a challenge it is to work
with, still works the best for running arbitrary jobs and I’ve been working
on some infrastructure experiments again,&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.omgubuntu.co.uk/2019/10/ubuntu-20-04-release-features&quot;&gt;Ubuntu 20.04 came out too&lt;/a&gt;, and it has a new installer. This lead to
some experiments with trying out the new automated installer method and
&lt;a href=&quot;https://nickcharlton.net/posts/automating-ubuntu-2004-installs-with-packer.html&quot;&gt;eventually this post once I made it all work&lt;/a&gt;. I’m now tracking
&lt;a href=&quot;https://github.com/hashicorp/packer/issues/9115&quot;&gt;an issue on Packer&lt;/a&gt;, &lt;a href=&quot;https://github.com/chef/bento/issues/1281#issuecomment-620388365&quot;&gt;an issue on Chef’s bento project&lt;/a&gt;,
&lt;a href=&quot;https://github.com/geerlingguy/packer-boxes/issues/52&quot;&gt;and another one too&lt;/a&gt;, as we try and make the new installer work
everywhere,&lt;/li&gt;
  &lt;li&gt;The feedback loop for debugging these things is possibly the worst I’ve ever
worked on: the only way to test if a theory works is to go through much of the
process repeatedly, which means waiting for 5-10 minutes every time. You end
up reading a lot whilst you wait,&lt;/li&gt;
  &lt;li&gt;In more open source news, I’m trying out having &lt;a href=&quot;https://github.com/thoughtbot/administrate&quot;&gt;Administrate&lt;/a&gt; “Office
Hours” by allocating two hours every other Friday for people to set a time
to come with an issue or pair on what I’m looking at. I’m starting this
internally only for now, but eventually I’d like to expand this out to
everyone. So, if you’re reading this, this is an invitation to ask!&lt;/li&gt;
  &lt;li&gt;With an injured leg for the past few weeks (caused by, I think, my old
running shoes being overdue for replacement), I’ve been off running for a
while now,&lt;/li&gt;
  &lt;li&gt;But, I’ve been cycling around an empty London instead, which is eerily
fascinating.&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Automating Ubuntu 20.04 installs with Packer</title>
        <link href="https://nickcharlton.net/posts/automating-ubuntu-2004-installs-with-packer.html" />
        <id>https://nickcharlton.net/posts/automating-ubuntu-2004-installs-with-packer.html</id>
        <published>Sun, 26 Apr 2020 00:00:00 +0000</published>
        <updated>Sun, 26 Apr 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Ubuntu 20.04 — which was released few days ago (23rd April) — brings with it a
new installer, replacing the previous &lt;a href=&quot;https://www.debian.org/devel/debian-installer/&quot;&gt;Debian installer&lt;/a&gt; with
&lt;a href=&quot;https://github.com/CanonicalLtd/subiquity&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subiquity&lt;/code&gt;&lt;/a&gt;. This means that any of the previous approaches for
automated/unattended installs no longer work and need to be replaced.&lt;/p&gt;

&lt;p&gt;No one seemed to have documented doing this successfully yet with &lt;a href=&quot;https://www.packer.io&quot;&gt;Packer&lt;/a&gt;,
so I set out to figure it out. But first, here’s a working unattended
configuration:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ubuntu-2004.json&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;builders&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ubuntu-2004&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;vmware-iso&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;guest_os_type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ubuntu-64&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;headless&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;iso_url&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://releases.ubuntu.com/20.04/ubuntu-20.04-live-server-amd64.iso&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;iso_checksum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;caf3fd69c77c439f162e2ba6040e9c320c4ff0d69aad1340a514319a9264df9f&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;iso_checksum_type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;sha256&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ssh_username&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ubuntu&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ssh_password&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ubuntu&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ssh_handshake_attempts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;20&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;http_directory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;memory&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;boot_wait&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;5s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;boot_command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;enter&amp;gt;&amp;lt;enter&amp;gt;&amp;lt;f6&amp;gt;&amp;lt;esc&amp;gt;&amp;lt;wait&amp;gt; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;autoinstall ds=nocloud-net;s=http://{{ .HTTPIP }}:{{ .HTTPPort }}/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;enter&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;provisioners&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;shell&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;inline&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ls /&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user-data&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#cloud-config&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;autoinstall&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;identity&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;hostname&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ubuntu-server&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1&apos;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ubuntu&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;network&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;network&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ethernets&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;ens33&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;dhcp4&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;dhcp-identifier&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mac&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ssh&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;install-server&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;late-commands&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;sed -i &apos;s/^#*\(send dhcp-client-identifier\).*$/\1 = hardware;/&apos; /target/etc/dhcp/dhclient.conf&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;sed&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-i&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;s/dhcp4:&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true/&amp;amp;\n&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;dhcp-identifier:&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mac/&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/target/etc/netplan/00-installer-config.yaml&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We also need the presence of the file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;meta-data&lt;/code&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;touch meta-data&lt;/code&gt; in the
directory available to the Packer HTTP server. The password is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ubuntu&lt;/code&gt;. Whilst
this uses the &lt;a href=&quot;https://www.packer.io/docs/builders/vsphere-iso.html&quot;&gt;VMware builder&lt;/a&gt;, the relevant configuration options exist
among the other builders, too.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/nickcharlton/packer-ubuntu-2004&quot;&gt;I’ve also created an example Github repo with the working configuration
in&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://wiki.ubuntu.com/FoundationsTeam/AutomatedServerInstalls&quot;&gt;new unattended installation is well documented&lt;/a&gt; and I started from the
&lt;a href=&quot;https://wiki.ubuntu.com/FoundationsTeam/AutomatedServerInstalls/QuickStart&quot;&gt;Quick Start guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There were four things that need solving on top of the basic configuration:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Ensure that there’s enough memory to run the installer with. 512MB caused a
kernel panic, 1GB works fine,&lt;/li&gt;
  &lt;li&gt;Install an SSH server,&lt;/li&gt;
  &lt;li&gt;Allow the &lt;a href=&quot;https://www.packer.io/docs/builders/vsphere-iso.html#ssh_handshake_attempts&quot;&gt;SSH handshake&lt;/a&gt; to fail for longer, because the installer has
it’s own SSH server which will cause “Waiting for SSH…” to connect to the
wrong thing and the build to fail,&lt;/li&gt;
  &lt;li&gt;Ensure that we have a persistent IP address after the installation is
completed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The typical way to do this is to restore the DHCP identifier used back to the
MAC address of the device, rather than the &lt;em&gt;device identifier&lt;/em&gt; which is now
common. &lt;a href=&quot;https://github.com/nickcharlton/boxes/commit/5b5d18ba146d081fe4eb4657b246aa6dd544455b&quot;&gt;This is the same problem as I’ve seen previously with Debian Buster
(10)&lt;/a&gt;, and I’ve reused the late command here to set the DHCP client to do
that. We also seem to need to do this with &lt;a href=&quot;https://netplan.io/&quot;&gt;netplan&lt;/a&gt; too, to reliably get
the same IP back that Packer is expecting.&lt;/p&gt;

&lt;p&gt;Interestingly, &lt;a href=&quot;https://github.com/CanonicalLtd/subiquity/blob/95c20226fdb74eef6cd780981299a5bbbaa426d2/subiquitycore/controllers/network.py&quot;&gt;subiquity does not seem to support &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dhcp-identifier&lt;/code&gt;&lt;/a&gt; and
so we need to do this ourselves after the installation is completed. We need to
quote the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sed&lt;/code&gt; line, as otherwise we &lt;a href=&quot;https://git.launchpad.net/cloud-init/tree/cloudinit/util.py#n954&quot;&gt;fall into a trap when loading
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-init&lt;/code&gt; configuration, as it seems to think it’s YAML&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This took quite a bit of time to get working right. To solve issues along
the way, I:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Used Alt + F2 to get a working console when the installer failed or got stuck,&lt;/li&gt;
  &lt;li&gt;Read the output of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/log/installer/subiquity-debug.log&lt;/code&gt; to get the
network configuration correct,&lt;/li&gt;
  &lt;li&gt;…and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/log/syslog&lt;/code&gt; to debug the YAML parsing issues around the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;late-commands&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #11</title>
        <link href="https://nickcharlton.net/posts/week-notes-11.html" />
        <id>https://nickcharlton.net/posts/week-notes-11.html</id>
        <published>Mon, 20 Apr 2020 00:00:00 +0000</published>
        <updated>Mon, 20 Apr 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;A little while ago, thoughtbot added a new team lead role and I started that
a week ago when we rolled that out into our London office. It’s exciting to
start doing something a bit different formally,&lt;/li&gt;
  &lt;li&gt;Read &lt;a href=&quot;https://boxofcrayons.com/the-coaching-habit-book/&quot;&gt;&lt;em&gt;The Coaching Habit&lt;/em&gt;&lt;/a&gt;; this was recommended to me by a friend a
month or so back and it’s all about giving less advice and asking more
questions. I ended up reading it on a sunny bank holiday afternoon in the
garden and I’d recommend it,&lt;/li&gt;
  &lt;li&gt;Less successfully, I spent some time on a bit of a folly of trying to write
a script to rebuild all gems; I have a lot of Rubies installed from jumping
around different projects and I &lt;em&gt;think&lt;/em&gt; Homebrew broke things by tidying up
OpenSSL. The main problem I faced is the shell just hanging, which is next to
impossible to debug. I ended up doing it all manually again and it took hours,&lt;/li&gt;
  &lt;li&gt;Over the long weekend, &lt;a href=&quot;https://github.com/vagrant-landrush/landrush/issues/334&quot;&gt;I solved an issue I’d opened way back in November
2018&lt;/a&gt;, which is my way of saying: yeah, I really do get around to things
eventually,&lt;/li&gt;
  &lt;li&gt;One of the problems of long weekends is that open source notifications
continue along: from a good state of being on top of them, as I write this
I’m far behind where I’d like to be,&lt;/li&gt;
  &lt;li&gt;This past Sunday (19th Apr), I went for a cycle around what’s now a very
empty London, through &lt;a href=&quot;https://www.strava.com/activities/3322717758&quot;&gt;a 30km loop from Hyde Park, up to Paddington and back
through Oxford St&lt;/a&gt; it’s very strange seeing areas which I’m used to being
so busy just being devoid of people,&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Configuring the Jenkins email-ext plugin with Groovy</title>
        <link href="https://nickcharlton.net/posts/configuring-jenkins-email-ext-plugin-groovy.html" />
        <id>https://nickcharlton.net/posts/configuring-jenkins-email-ext-plugin-groovy.html</id>
        <published>Mon, 20 Apr 2020 00:00:00 +0000</published>
        <updated>Mon, 20 Apr 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;The default email notifications provided by &lt;a href=&quot;https://jenkins.io&quot;&gt;Jenkins&lt;/a&gt; are a little
inflexible, but the &lt;a href=&quot;https://github.com/jenkinsci/email-ext-plugin&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;email-ext&lt;/code&gt; plugin&lt;/a&gt; adds a whole host of configuration
options. &lt;a href=&quot;https://github.com/nickcharlton/jenkins-dsl&quot;&gt;I use this to provide the build log in some sync jobs I have&lt;/a&gt;, but
I wanted to configure this via a script in Jenkins’ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init.groovy.d&lt;/code&gt; directory
to automate the configuration.&lt;/p&gt;

&lt;p&gt;I’ve leaned &lt;a href=&quot;https://pghalliday.com/jenkins/groovy/sonar/chef/configuration/management/2014/09/21/some-useful-jenkins-groovy-scripts.html&quot;&gt;this article&lt;/a&gt;, &lt;a href=&quot;https://gist.github.com/johnbuhay/c6213d3d12c8f848a385&quot;&gt;this Gist about setting up users&lt;/a&gt; and &lt;a href=&quot;https://gist.github.com/chrisvire/383a2c7b7cfb3f55df6a&quot;&gt;this
other Gist about configuring credentials&lt;/a&gt; as I’ve done this before, but I
couldn’t find anything which pulled together how you’d configure &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;email-ext&lt;/code&gt;.
But I did come across &lt;a href=&quot;https://issues.jenkins-ci.org/browse/JENKINS-39147&quot;&gt;this now implemented issue which exposes the important
bits of what we’ll need to configure&lt;/a&gt;, so a combination of the other
examples lead me to come up with the following:&lt;/p&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;jenkins.model.Jenkins&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inst&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Jenkins&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;emailExt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getDescriptor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;hudson.plugins.emailext.ExtendedEmailPublisher&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;emailExt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setSmtpAuth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;username&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                     &lt;span class=&quot;s2&quot;&gt;&quot;password&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;emailExt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setDefaultReplyTo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;jenkins@example.com&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;emailExt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setSmtpServer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;smtp.example.com&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;emailExt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setUseSsl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;emailExt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setSmtpPort&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;587&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;emailExt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setCharset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;emailExt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setDefaultRecipients&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;someone@example.com&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;emailExt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…which allows you to configure the basics of the account setup. This is loading
the current instant of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExtendedEmailPublisherDescriptor&lt;/code&gt;, so &lt;a href=&quot;https://github.com/jenkinsci/email-ext-plugin/blob/master/src/main/java/hudson/plugins/emailext/ExtendedEmailPublisherDescriptor.java&quot;&gt;any of the
setters in that class can be called this way&lt;/a&gt;.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #10</title>
        <link href="https://nickcharlton.net/posts/week-notes-10.html" />
        <id>https://nickcharlton.net/posts/week-notes-10.html</id>
        <published>Mon, 06 Apr 2020 00:00:00 +0000</published>
        <updated>Mon, 06 Apr 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;Double digits! I’ve written ten of these now. It’s gradually becoming a
habit, especially once I’ve noted at least one thing down during the week,&lt;/li&gt;
  &lt;li&gt;This week I moved everything over to &lt;a href=&quot;https://getdrafts.com&quot;&gt;Drafts&lt;/a&gt;. Previously I was using
&lt;a href=&quot;https://bear.app&quot;&gt;Bear&lt;/a&gt; (which is a great app), and then sort of using both tools whilst I
figured this out,&lt;/li&gt;
  &lt;li&gt;But the interesting bit isn’t the tool, it’s the habit formation: I write
nearly everything first in Drafts before moving it elsewhere (be it Basecamp
messages, GitHub issue replies or emails), keep notes on everything during
the day and use it to write out my thoughts before saying them,&lt;/li&gt;
  &lt;li&gt;We’re now three weeks into full working from home because of COVID-19. I’m
used to it now, even though I was struggling with the lack of my usual level
of exercise and then not being able to sleep very well,&lt;/li&gt;
  &lt;li&gt;I join more remote calls from my desk now, so now I’m wondering how to make
remote calls better, should I &lt;a href=&quot;https://aaronshekey.com/posts/video-conferencing-with-a-proper-camera/&quot;&gt;upgrade the video&lt;/a&gt;, &lt;a href=&quot;https://ma.tt/2020/03/dont-mute-get-a-better-headset/&quot;&gt;or the audio&lt;/a&gt;, or
both?&lt;/li&gt;
  &lt;li&gt;We started a new planning approach &lt;a href=&quot;https://github.com/thoughtbot/administrate/projects/1&quot;&gt;on Administrate by starting to use GitHub
projects&lt;/a&gt;. The intention here is to highlight work-in-progress, which was
previously hidden, and to set expectations for if something is soon likely
to make it into a release,&lt;/li&gt;
  &lt;li&gt;I use &lt;a href=&quot;https://octobox.io&quot;&gt;Octobox&lt;/a&gt; for keeping on top of my GitHub notifications and for the
first time in years, I got to inbox zero! For the last few months, I’d been
hovering around 25 items much of the time, but was never able to get it any
lower. As I write this it’s 3 things.&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #9</title>
        <link href="https://nickcharlton.net/posts/week-notes-9.html" />
        <id>https://nickcharlton.net/posts/week-notes-9.html</id>
        <published>Mon, 23 Mar 2020 00:00:00 +0000</published>
        <updated>Mon, 23 Mar 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;This was the first full week of working from home for me in years. I used to
do this when I freelanced, which whilst a while ago often seems to help in
surprising ways,&lt;/li&gt;
  &lt;li&gt;I am seeing meetings which were a mix of colocated and remote run better and
gradually finding work becoming more asynchronous, which is all great to see.
These are things that’ll make everything better in the long run, once the
immediacy is over.&lt;/li&gt;
  &lt;li&gt;But, never before has writing “take care” in an email to a relative stranger
actually meant something,&lt;/li&gt;
  &lt;li&gt;Because of COVID-19, &lt;a href=&quot;https://thefocuscourse.com/free-time-management-course/&quot;&gt;The Focus on Time course&lt;/a&gt; opened up for free and I
jumped right onto that. I’m finding I have more time now (no commute!), but
I’d already not been using my time quite how I’d wanted. I’m not too far in
yet, but it’s good so far!&lt;/li&gt;
  &lt;li&gt;Finally, I switched to using &lt;a href=&quot;https://github.com/neoclide/coc.nvim&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;coc.nvim&lt;/code&gt;&lt;/a&gt;, which brings support for
IntelliSense over from VS Code. I’d been struggling with “modern” JavaScript
tooling anyway, but decided to take the leap. &lt;a href=&quot;https://github.com/nickcharlton/dotfiles/commit/1497dab31ba316dad1312372c09a111d653809f9&quot;&gt;I’m pleased I didn’t need to
configure much&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #8</title>
        <link href="https://nickcharlton.net/posts/week-notes-8.html" />
        <id>https://nickcharlton.net/posts/week-notes-8.html</id>
        <published>Sun, 15 Mar 2020 00:00:00 +0000</published>
        <updated>Sun, 15 Mar 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;This was the week where Coronavirus/COVID-19 mitigations really stepped up,
especially for people (like me!) working for predominantly US companies. In
the absence of stronger advice and organisations where working remotely is
relatively easy, this is a good move,&lt;/li&gt;
  &lt;li&gt;On that, we moved to being fully remote for the foreseeable future. I’ve got
a pretty good setup in this regard as I already do this once a week.
Ergonomics are important to me; if I just work on my laptop I get tired
faster and my back aches horribly,&lt;/li&gt;
  &lt;li&gt;In other news, I published an &lt;a href=&quot;https://github.com/advisories/GHSA-2p5p-m353-833w&quot;&gt;Administrate security vulnerability this
week&lt;/a&gt;. This is the first time I’ve gone through this process, which
started a couple of weeks back with Benoit Côté-Jodoin from Shopify emailing
us to let us know. I then ended up using an interview to pair on the
potential solution and using &lt;a href=&quot;https://help.github.com/en/github/managing-security-vulnerabilities/collaborating-in-a-temporary-private-fork-to-resolve-a-security-vulnerability&quot;&gt;GitHub’s new private fork functionality&lt;/a&gt;.
All in all, the most difficult part of this process was writing up the
advisory itself, making something clear and not accidentally withhold
something important.&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #7</title>
        <link href="https://nickcharlton.net/posts/week-notes-7.html" />
        <id>https://nickcharlton.net/posts/week-notes-7.html</id>
        <published>Mon, 02 Mar 2020 00:00:00 +0000</published>
        <updated>Mon, 02 Mar 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;Last week, I released a &lt;a href=&quot;https://github.com/nickcharlton/administrate-field-nested_has_many/releases/tag/v1.2.0&quot;&gt;new version&lt;/a&gt; of
&lt;a href=&quot;https://github.com/nickcharlton/administrate-field-nested_has_many&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;administrate-field-nested_has_many&lt;/code&gt;&lt;/a&gt;,&lt;/li&gt;
  &lt;li&gt;These last two weeks have been interesting in that there’s been lots going
on …and none of that I can publish. After doing this seven times, I’m pleased
about how much I really do operate in the open,&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://tailwindui.com&quot;&gt;Tailwind UI&lt;/a&gt; came out this week, I’ve been following along through the
&lt;a href=&quot;https://artofproductpodcast.com&quot;&gt;Art of Product podcast&lt;/a&gt; and spent some time playing around with it this
weekend. I’m still on the fence with &lt;a href=&quot;https://tailwindcss.com&quot;&gt;Tailwind&lt;/a&gt;. It’s a utility-first CSS
framework and so to work with it you add classes to build up what you need.
For prototyping this means you’ll write a &lt;em&gt;lot&lt;/em&gt; of classes, but these can
then be &lt;a href=&quot;https://tailwindcss.com/components&quot;&gt;extracted out into components&lt;/a&gt;. I haven’t quite done enough with
it yet, but I’m undeniably productive with it and so I’m holding back my
old-school don’t-mix-content-with-presentation ethos,&lt;/li&gt;
  &lt;li&gt;Finally, I switched to &lt;a href=&quot;https://www.jetbrains.com/lp/mono/&quot;&gt;Jetbrains Mono&lt;/a&gt; as my terminal font. Ligatures
finally make error handling in Go and the awkward bits of JavaScript look
nice!&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #6</title>
        <link href="https://nickcharlton.net/posts/week-notes-6.html" />
        <id>https://nickcharlton.net/posts/week-notes-6.html</id>
        <published>Sun, 16 Feb 2020 00:00:00 +0000</published>
        <updated>Sun, 16 Feb 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;With &lt;a href=&quot;https://en.wikipedia.org/wiki/Storm_Ciara&quot;&gt;Storm Ciara&lt;/a&gt; bringing wind gusts of up to 60mph, I ended up not
cycling all week. There’s not much fun (and safe) cycling to be had in those
sorts of wind speeds,&lt;/li&gt;
  &lt;li&gt;I spent a good chunk of time this week working on &lt;a href=&quot;https://github.com/thoughtbot/appraisal&quot;&gt;Appraisal&lt;/a&gt;, and I’m
really happy about that. It’s been falling behind with everything since
&lt;a href=&quot;https://bundler.io/blog/2019/01/03/announcing-bundler-2.html&quot;&gt;Bundler 2.0&lt;/a&gt; was released, and my approach of ignoring it wasn’t going to
last forever. I closed a bunch of PRs, opened a few &lt;a href=&quot;https://github.com/thoughtbot/appraisal/pull/166&quot;&gt;more related to Ruby
versions&lt;/a&gt; and &lt;a href=&quot;https://github.com/thoughtbot/appraisal/pull/165&quot;&gt;accepting paths&lt;/a&gt; (which should &lt;a href=&quot;https://github.com/thoughtbot/administrate/pull/1549&quot;&gt;speed up builds on
Administrate&lt;/a&gt;),&lt;/li&gt;
  &lt;li&gt;For many months, I’ve been working on replacing our office internet and
network and this week, because we’re also building a new meeting room it
stepped up a gear into planning out what we should do. It’s quite fun doing
this stuff again (I did this sort of thing early in my career). But perhaps
I’ll think differently after rewiring a forty-two port patch panel,&lt;/li&gt;
  &lt;li&gt;All of this is in contrast to spending the main part of this week regularly
stuck on what I’m supposed to be working on. Some parts through architectural
complexity, some through people not being around and some from
less-than-ideal prior planning. Here’s to hoping we can solve that next week.&lt;/li&gt;
  &lt;li&gt;I enjoyed reading about &lt;a href=&quot;https://twitter.com/radiomorillo/status/1227969004213587970&quot;&gt;The Wall of Technical Debt&lt;/a&gt; this week. Unlike a
lot of articles, it’s correct around what technical debt really is (a set of
trade-offs) and (most importantly), has some nice suggestions around exposing
the costs to everyone on the team,&lt;/li&gt;
  &lt;li&gt;And also &lt;a href=&quot;https://doriantaylor.com/agile-as-trauma&quot;&gt;Agile as Trauma&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/radiomorillo/status/1227969004213587970&quot;&gt;this associated Twitter thread&lt;/a&gt;, which
talks about the position the Agile manifesto comes from and some of the
drawbacks today. I’ve seen a lot of the points mentioned.&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #5</title>
        <link href="https://nickcharlton.net/posts/week-notes-5.html" />
        <id>https://nickcharlton.net/posts/week-notes-5.html</id>
        <published>Sun, 09 Feb 2020 00:00:00 +0000</published>
        <updated>Sun, 09 Feb 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;Some things are a blessing in disguise: we started the week blocked on our
current piece of work, which &lt;a href=&quot;https://nickcharlton.net/posts/week-notes-4.html&quot;&gt;after the hectic week previously&lt;/a&gt; really
helped,&lt;/li&gt;
  &lt;li&gt;This was because we were blocked on everything as the feature we’re working
on depends on an API, which depends on the release of a service rewrite and
the next (and final) bit of work is the same as we’ve already done. We could
certainly &lt;em&gt;do more&lt;/em&gt; but also achieve &lt;em&gt;less&lt;/em&gt;, it’s a tough one but was the
right call,&lt;/li&gt;
  &lt;li&gt;Tuesday morning was entertaining: the combination of a strong headwind and
last weekend meant cycling was the worst it’s been in a very long time and I
barely could get up the hills,&lt;/li&gt;
  &lt;li&gt;I &lt;a href=&quot;https://github.com/nickcharlton/dotfiles/pull/26&quot;&gt;finished the first pass at some tooling&lt;/a&gt; I’m working on to improve my
maintainer workflow. It allows you to check out a contributors’ pull request
in your local clone, whilst maintaining your ability to push changes back to
their branch. I need to write it all up yet and give it a spin for a couple
of weeks to check it works well but so far I’m pretty pleased with it,&lt;/li&gt;
  &lt;li&gt;This week, I started on an bike interval training regime as I try and build
up my bike fitness. Unfortunately, this means I won’t be running again for
quite a while (trying to fit both in is …not practical),&lt;/li&gt;
  &lt;li&gt;On Thursday, I overzealous in merging some work I’d been working on without
getting it QA’ed. It did, of course, break. With it being both paired on and
test-driven, I came at it with a lot of confidence that it’d be fine. Our
bit was, sadly the requests it makes goes to a service not quite as reliable,&lt;/li&gt;
  &lt;li&gt;I enjoyed reading
&lt;a href=&quot;https://www.g9labs.com/2020/01/04/high-output-management-for-non-managing-tech-leads/&quot;&gt;Andrew Hao’s &lt;em&gt;High Output Management for (Non-managing) Tech Leads&lt;/em&gt;&lt;/a&gt; this
week. I find a lot of leadership/management articles useful in a consulting
context (although, often with a bit of a leap and with the passage of
time). As a consultant, I’m always on the look out for work of high leverage
(especially when this means ignoring fun but not-valuable work). Similarly,
the questions to ask around output indicators are helpful (e.g.: “What do
you care about seeing?”, “What worries you the most about our team?”)&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #4</title>
        <link href="https://nickcharlton.net/posts/week-notes-4.html" />
        <id>https://nickcharlton.net/posts/week-notes-4.html</id>
        <published>Sun, 02 Feb 2020 00:00:00 +0000</published>
        <updated>Sun, 02 Feb 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;em&gt;I decided to skip last week’s. Nothing of interest happened, outside of the
weekend which fits just as well here.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Spent last weekend with my Mum, which was lovely. We started off a batch of
homemade wine, which was great fun,&lt;/li&gt;
  &lt;li&gt;Started using &lt;a href=&quot;https://getdrafts.com&quot;&gt;Drafts&lt;/a&gt; on inspiration from &lt;a href=&quot;https://www.interroban.gg/&quot;&gt;Luke&lt;/a&gt;. I’d previously been
using &lt;a href=&quot;https://bear.app&quot;&gt;Bear&lt;/a&gt; (which I’ve really liked), but I’ve been curious about
Draft’s actions since starting these Week Notes and automating what steps
there are (manually breaking out long lines with links is a tedious process),&lt;/li&gt;
  &lt;li&gt;I saw &lt;a href=&quot;https://en.wikipedia.org/wiki/1917_(2019_film)&quot;&gt;&lt;em&gt;1917&lt;/em&gt; &lt;/a&gt; on Tuesday. It’s a fascinating movie. The single shot
style really draws you in and puts you right there with how bleak the WW1
battlefield must have been. It’s been a while since a film has stuck with me
for several days, too,&lt;/li&gt;
  &lt;li&gt;This weekend was spent in &lt;a href=&quot;https://www.strava.com/activities/3066898234&quot;&gt;mid-Wales, cycling a loop from Brecon&lt;/a&gt; to
&lt;a href=&quot;https://www.mountainbothies.org.uk/bothies/wales/moel-prysgau/&quot;&gt;&lt;em&gt;Moel Prysgau&lt;/em&gt;&lt;/a&gt; bothy and then back to Brecon again. It was a very fun
trip, starting off on the road, then some gravel forestry tracks towards the
bothy. On the Sunday, we decided to go off-road and ended up fording four or
five rivers working our way through. After that, we got back on the road to
build up some momentum, eventually heading up through a very windy
&lt;a href=&quot;https://en.wikipedia.org/wiki/Sennybridge_Training_Area&quot;&gt;Sennybridge ranges&lt;/a&gt; on the way back to Brecon,&lt;/li&gt;
  &lt;li&gt;This trip was helpful in making me realise where the shortcomings of my
cycling fitness are. I need to do some long rides to improve my endurance
and some time doing some hill-training wouldn’t hurt either,&lt;/li&gt;
  &lt;li&gt;It also made me realise how bad it is if you don’t give it enough time to
pack before leaving. I forgot a bunch of things (a mug, fork, etc.) and
could’ve done with some spare shoes (my feet got very wet), which wouldn’t
have been a problem if I’d been able to take my time,&lt;/li&gt;
  &lt;li&gt;Anyway, for various reasons that ended up being a very busy and hectic week.
Let’s hope the next is easier…&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #3</title>
        <link href="https://nickcharlton.net/posts/week-notes-3.html" />
        <id>https://nickcharlton.net/posts/week-notes-3.html</id>
        <published>Sun, 19 Jan 2020 00:00:00 +0000</published>
        <updated>Sun, 19 Jan 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;New office, new cycle route for the first half of the week. This always
throws out the routine for a little bit, but you get used to it soon enough,&lt;/li&gt;
  &lt;li&gt;With this project I’m trying out a new tactic: &lt;a href=&quot;https://martinfowler.com/articles/on-pair-programming.html&quot;&gt;pairing by default&lt;/a&gt;. I’m
starting from a position of not being confident in the technologies we’re
using, so that’s helping me. Plus I’m trying to solve a problem I regularly
have, which is to fall deeply into a specific problem at the expense of the
overall goal. I’m hoping by instead asking, “shall we pair on this?” we ask
“why shouldn’t we be pairing on this?”,&lt;/li&gt;
  &lt;li&gt;This week I really enjoyed &lt;a href=&quot;http://jetfuel.metalbat.com/blah/ftd.html&quot;&gt;Cadence&lt;/a&gt; by &lt;a href=&quot;http://metalbat.com/&quot;&gt;William Van Hecke&lt;/a&gt;, who
previously worked on &lt;a href=&quot;https://www.omnigroup.com/omnifocus&quot;&gt;OmniFocus&lt;/a&gt;. It fits well into what I’ve been starting
to do recently: focusing on building habits (&lt;a href=&quot;https://fs.blog/2017/06/habits-vs-goals/&quot;&gt;something I see&lt;/a&gt;
&lt;a href=&quot;https://www.nateberkopec.com/blog/2020/01/02/growth-over-results.html&quot;&gt;come up more often&lt;/a&gt;), thinking about the long-scale things I want to do
and reviewing it regularly. But also: keeping &lt;em&gt;loads&lt;/em&gt; of notes (mostly with
&lt;a href=&quot;https://www.notion.so/?r=674e8a7394b245df814de19dc5bbce1f&quot;&gt;Notion&lt;/a&gt;, some on paper), using &lt;a href=&quot;https://www.amazon.co.uk/gp/product/B003JMW1KS/ref=as_li_ss_tl?ie=UTF8&amp;amp;linkCode=ll1&amp;amp;tag=nisbl-21&amp;amp;linkId=ff91686b6dca17156cfca0b07bf4655a&amp;amp;language=en_GB&quot;&gt;the crappiest notebook I could get myself
to like&lt;/a&gt; and staying no to things that don’t fit in the bigger plan,&lt;/li&gt;
  &lt;li&gt;Related to that, this week I planned out three &lt;a href=&quot;https://bikepacking.com&quot;&gt;bikepacking&lt;/a&gt; trips and a
weekend to brew some beer (something which without planning is impossible to
make happen), but also decided to not do another handful of things. It’s a
weird feeling.&lt;/li&gt;
  &lt;li&gt;Tortoise did a good series on Trees; about planting, to capture CO2 and the
science behind it. &lt;a href=&quot;https://members.tortoisemedia.com/2020/01/13/plant-a-trillion-trees/content.html?sig=VTOKkrC64ZvSFPmYSnF3ricMmGkY59TeAMQQZTTpD78&amp;amp;utm_source=Twitter&amp;amp;utm_medium=Social&amp;amp;utm_campaign=12Jan2020&amp;amp;utm_content=trees&quot;&gt;I’d recommend starting with &lt;em&gt;Plant a Trillion
Trees&lt;/em&gt;&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;And related to that, this week I started (and then finished!) reading
&lt;a href=&quot;https://www.amazon.co.uk/gp/product/1856232123/ref=as_li_ss_tl?ie=UTF8&amp;amp;linkCode=ll1&amp;amp;tag=nisbl-21&amp;amp;linkId=55e03e1faf330e0eed6af4b436fbc932&amp;amp;language=en_GB&quot;&gt;Getting Started in Your Own Wood&lt;/a&gt;. Owning a wood is one of those ideas
that comes back to me every so often, did so again recently, and this time I
decided to go a bit deeper into working out what that might be like. Still
much, much more to look into.&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #2</title>
        <link href="https://nickcharlton.net/posts/week-notes-2.html" />
        <id>https://nickcharlton.net/posts/week-notes-2.html</id>
        <published>Sun, 12 Jan 2020 00:00:00 +0000</published>
        <updated>Sun, 12 Jan 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;ul&gt;
  &lt;li&gt;Started on a new project this week. I can’t tell you about it, other than
I’ll be writing &lt;a href=&quot;https://reactjs.org&quot;&gt;React&lt;/a&gt;. I’ve traditionally struggled to do enough for the
knowledge to end up sticking, so I’m hoping this will help,&lt;/li&gt;
  &lt;li&gt;I completed my goals planning for the year. Every year I do this it gets
easier, and this year I’m especially pleased with how specific and actionable
they’ve ended up,&lt;/li&gt;
  &lt;li&gt;Two days into cycling to work ended up with a puncture on the way home.
Turned out to likely be something I missed previously: I use
&lt;a href=&quot;https://www.conti-tyres.co.uk/commuting-touring/gatorskin&quot;&gt;Continental Gatorskins&lt;/a&gt;, which has a wire mesh and I think I ran over a
pin back in December, and the mesh was exposed on the inside. I tidied that
up, applied some &lt;a href=&quot;&quot;&gt;gorilla tape&lt;/a&gt; as precaution and was back on the go.
Let’s see how long it lasts this time!&lt;/li&gt;
  &lt;li&gt;I spent a bunch of time this week trying to improve my GitHub pull request
reviewing workflow (I often do very minor edits to people’s contributions
before merging to help get them over the line) and ended up instead blowing
away someone’s changes. Not the most enjoyable thing to do on a Friday
afternoon! I’ll write this all up once I eventually figure out the best
workflow.&lt;/li&gt;
  &lt;li&gt;Finally, I got my main bike back (it’d been in storage at my parents’ since
doing the &lt;a href=&quot;https://www.londonbrightoncycle.co.uk&quot;&gt;London to Brighton&lt;/a&gt; last year), so long rides are an option
again.&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Week Notes #1</title>
        <link href="https://nickcharlton.net/posts/week-notes-1.html" />
        <id>https://nickcharlton.net/posts/week-notes-1.html</id>
        <published>Sun, 05 Jan 2020 00:00:00 +0000</published>
        <updated>Sun, 05 Jan 2020 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Several people I follow post &lt;a href=&quot;https://weeknot.es&quot;&gt;#weeknotes&lt;/a&gt;, which I’ve always found a
particularly interesting insight into what people are doing. I thought I’d
give it a go.&lt;/p&gt;

&lt;p&gt;I plan to do a mix of work (when I can post about it), open source,
side projects and fitness &amp;amp; adventure topics, which I hope will keep it
interesting. Right. Here we go…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I’ve been spending the past week diving deep into &lt;a href=&quot;https://github.com/thoughtbot/administrate&quot;&gt;Administrate&lt;/a&gt;’s issues
and open PRs, looking for trends and building a taxonomy of labels,&lt;/li&gt;
  &lt;li&gt;To do this, I ended up printing 204 index cards!&lt;/li&gt;
  &lt;li&gt;So far, I’ve closed around 40 issues and PRs (most of which were out of date
or duplicates),&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://danielgroves.net&quot;&gt;Dan&lt;/a&gt; and I entered into the &lt;a href=&quot;https://frontier300.cc&quot;&gt;Frontier 300&lt;/a&gt;, a coast to coast 300km
off-road cycle race from the Irish Sea to the North Sea in June,&lt;/li&gt;
  &lt;li&gt;We then planned a weekend of &lt;a href=&quot;https://bikepacking.com/routes/new-forest-gravel-taster-uk/&quot;&gt;bikepacking in the New Forest&lt;/a&gt;
for March,&lt;/li&gt;
  &lt;li&gt;And spent a long time talking about kit,&lt;/li&gt;
  &lt;li&gt;I finished reading &lt;a href=&quot;https://www.amazon.co.uk/Man-Who-Cycled-World/dp/0552158445/ref=as_li_ss_tl?ie=UTF8&amp;amp;linkCode=ll1&amp;amp;tag=nisbl-21&amp;amp;linkId=3b3439af35541f97937ccb2bd347c10f&amp;amp;language=en_GB&quot;&gt;The Man Who Cycled The World&lt;/a&gt;, I enjoyed it a lot,&lt;/li&gt;
  &lt;li&gt;At the start of the year, I always spend some time setting goals and thinking
about what I want to do. I’ve done so again this year, with plans for many
more long cycling trips, a side project I want to launch, getting a driving
license (which I’ve said I’d do for years!) and a few more things. I didn’t
quite yet.&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Tailwind CSS with Rails 6 and Webpacker</title>
        <link href="https://nickcharlton.net/posts/tailwind-css-rails-webpacker.html" />
        <id>https://nickcharlton.net/posts/tailwind-css-rails-webpacker.html</id>
        <published>Sun, 10 Nov 2019 00:00:00 +0000</published>
        <updated>Sun, 10 Nov 2019 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I don’t do many things frontend these days, but I’ve wanted to try out
&lt;a href=&quot;https://tailwindcss.com&quot;&gt;Tailwind&lt;/a&gt; for a while, and I finally had the opportunity. Alas, it was a
Rails app which had no frontend at all (apart from &lt;a href=&quot;https://github.com/thoughtbot/administrate&quot;&gt;administrate&lt;/a&gt;), so I
need to start from the very beginning. Here’s how I did it:&lt;/p&gt;

&lt;h2 id=&quot;webpacker&quot;&gt;Webpacker&lt;/h2&gt;

&lt;p&gt;I needed to add &lt;a href=&quot;https://github.com/rails/webpacker&quot;&gt;Webpacker&lt;/a&gt;, as I’d initially not done this when generating
the application. You should be able to skip this if you already have a working
setup. First, I added &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;webpacker&lt;/code&gt; to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile&lt;/code&gt; and then ran the generator:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bundle &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;rails webpacker:install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This command configures much of what’s needed. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Webpacker&lt;/code&gt; directory
layout ends up looking like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;app/javascript
├── packs
│   └── application.js
└── src
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;packs&lt;/code&gt; contain the collection of functionality processed with Webpacker to
build the package of JavaScript (and CSS!) that serves your application. The
default one being &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;application.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app/javascript/packs/application.js&lt;/code&gt;, I have the following:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@rails/ujs&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;turbolinks&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then, in the application layout, we’ll add the two hooks required to get
JavaScript and styles included:&lt;/p&gt;

&lt;div class=&quot;language-erb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stylesheet_pack_tag&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;application&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;media: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;all&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;data-turbolinks-track&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;reload&quot;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;javascript_pack_tag&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;application&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;data-turbolinks-track&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;reload&quot;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which loads both the JavaScript &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;application&lt;/code&gt; pack and the separated stylesheet.
These replace any existing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stylesheet_link_tag&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;javascript_link_tag&lt;/code&gt; usage.&lt;/p&gt;

&lt;h2 id=&quot;tailwind-css&quot;&gt;Tailwind CSS&lt;/h2&gt;

&lt;p&gt;Then, I added &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tailwindcss&lt;/code&gt; to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.json&lt;/code&gt; using Yarn:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;yarn add tailwindcss &lt;span class=&quot;nt&quot;&gt;--dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And generated a Tailwind CSS config file:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npx tailwindcss init app/javascript/src/tailwind.config.js
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The configuration file starts empty and looks like this:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;theme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;variants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To setup Tailwind, I’m importing the suggested imports in
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app/javascript/css/application.css&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;@import&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&quot;tailwindcss/base&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@import&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&quot;tailwindcss/components&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@import&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&quot;tailwindcss/utilities&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Webpacker is using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;postcss-import&lt;/code&gt;, so here I’m not using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@tailwind&lt;/code&gt; to pull
in the dependency &lt;a href=&quot;https://tailwindcss.com/docs/installation/#2-add-tailwind-to-your-css&quot;&gt;as suggested in the documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To configure PostCSS, I needed to add Tailwind in the right place (and
overwrite the default location it loads the config file from):&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;autoprefixer&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;postcss-import&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;tailwindcss&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./app/javascript/src/tailwind.config.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;postcss-flexbugs-fixes&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;postcss-preset-env&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)({&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;autoprefixer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;flexbox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;no-2009&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, I needed to configure Webpacker to expose a standalone CSS file for
every environment in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default&lt;/code&gt; section of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/webpacker.yml&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Extract and emit a css file&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;extract_css&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(You can also to delete the same line overridden in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;production&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;Asset management in Rails is gradually moving towards Webpacker, and this is
probably a good thing — standardising around the most common tools used
in the JavaScript community. But it’s still a bit confusing if this isn’t
something do you often.&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
        <title>Token Authentication with Rails</title>
        <link href="https://nickcharlton.net/posts/token-authentication-in-rails.html" />
        <id>https://nickcharlton.net/posts/token-authentication-in-rails.html</id>
        <published>Fri, 08 Nov 2019 00:00:00 +0000</published>
        <updated>Fri, 08 Nov 2019 00:00:00 +0000</updated>
        <summary type="html">&lt;blockquote&gt;
  &lt;p&gt;How can you do token authentication with Rails out of the box?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://thoughtbot.com/blog/token-authentication-with-rails&quot;&gt;See on Giant Robots&lt;/a&gt;&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Some resources for learning Go</title>
        <link href="https://nickcharlton.net/posts/learning-go-resources.html" />
        <id>https://nickcharlton.net/posts/learning-go-resources.html</id>
        <published>Sun, 20 Oct 2019 00:00:00 +0000</published>
        <updated>Sun, 20 Oct 2019 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I’m starting on a Go project for the first time in a while, and a few people
have asked me how to starting writing Go. The last time I spent a significant
amount of time writing Go was over a year ago, so while I also needed to
refresh my knowledge, there have been a few new things released since.&lt;/p&gt;

&lt;p&gt;I learn best in two phases: first reading a lot about a subject and then
building something with it. The project itself is the last bit, but going
into it I want to be as ready as I can to hit the ground running. That all
said, &lt;em&gt;what&lt;/em&gt; I read is essential. I don’t find programming books which walk
through things like type primitives in great depth remotely engaging —
I want to know just enough to be productive.&lt;/p&gt;

&lt;p&gt;I’m assuming that you’re coming from the position of already being confident
with a programming language and testing. I don’t think I’d start with
learning Go from the beginning.&lt;/p&gt;

&lt;h3 id=&quot;learn-go-with-tests&quot;&gt;&lt;a href=&quot;https://quii.gitbook.io/learn-go-with-tests/&quot;&gt;Learn Go with Tests&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Learn Go with Tests&lt;/em&gt; provides a lovely introduction to the basics of Go while
taking you through test-driving what you’re learning. This is the first time
I’ve seen this approach, and I’ve found it’s helped me get back into the
language itself.&lt;/p&gt;

&lt;h3 id=&quot;go-in-practice&quot;&gt;&lt;a href=&quot;http://goinpracticebook.com&quot;&gt;Go in Practice&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;Once you’ve got an understanding of the basics of Go and have written a little,
I’d recommend &lt;em&gt;Go in Practice&lt;/em&gt;. It’ll run through some common things you’ll
need to do and expose you to some patterns as you go. You might well find you
can use the examples mostly as-is, which I find helps in making it feel like
you’re making good progress in building something.&lt;/p&gt;

&lt;p&gt;I read this cover to cover, but you don’t need to do that. I find once I have,
it’s much easier for me to pick the book up and refer to something I
then need.&lt;/p&gt;

&lt;h3 id=&quot;ultimate-go-programming-course&quot;&gt;Ultimate Go Programming Course&lt;/h3&gt;

&lt;p&gt;Once I’ve built a few simple projects, I then want to go into some more depth
on how to structure more complex applications. I find this sort of depth
doesn’t help until much later on and watching a video course helps keep me
engaged.&lt;/p&gt;

&lt;p&gt;I’ve been finding Bill Kennedy’s &lt;em&gt;Ultimate Go Programming&lt;/em&gt; fit this well, he’s
an engaging teacher and the depth is just right.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://learning.oreilly.com/videos/ultimate-go-programming/9780134757476&quot;&gt;If you have an O’Reilly subscription, you can find it there.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/ardanlabs/gotraining&quot;&gt;And notes and slides on GitHub.&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;go-by-example&quot;&gt;&lt;a href=&quot;https://gobyexample.com&quot;&gt;Go by Example&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;Finally, something I refer to regularly that’ll help fill in some gaps. I jump
to &lt;em&gt;Go by Example&lt;/em&gt; when I broadly know what I’m trying to do, but can’t quite
figure out how to do it in Go.&lt;/p&gt;

&lt;p&gt;It’s how I solve something like: “I need to read a file …how do I do that?”&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
        <title>Structuring Terraform with Remote State</title>
        <link href="https://nickcharlton.net/posts/structuring-terraform-remote-state.html" />
        <id>https://nickcharlton.net/posts/structuring-terraform-remote-state.html</id>
        <published>Fri, 11 May 2018 00:00:00 +0000</published>
        <updated>Fri, 11 May 2018 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I’ve been using &lt;a href=&quot;https://www.terraform.io&quot;&gt;Terraform&lt;/a&gt; for just about four years at this point, but
outside working with other organisations’ configuration, I’ve not sat down and
built something from scratch since the very beginning. Then, I’d only had a
single environment that was configuring DNS for all of my domains or was
putting together examples for &lt;a href=&quot;/posts/terraform-aws-vpc.html&quot;&gt;other&lt;/a&gt;
&lt;a href=&quot;/posts/kubernetes-terraform-google-cloud.html&quot;&gt;projects&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But most recently, I’ve wanted to pull this configuration together as I’ve been
experimenting with &lt;a href=&quot;/posts/kubernetes-terraform-google-cloud.html&quot;&gt;Kubernetes on Google Cloud&lt;/a&gt; and to have
something to point to when recommending to folks just starting to build out
their configurations.&lt;/p&gt;

&lt;p&gt;A common problem I’d been seeing is mistakes in one environment breaking others
(nothing like building test cluster and nearly bring down production!) and when
splitting by environment, struggling to share common configuration between
them. When collaborating across teams, all of these problems are exacerbated.&lt;/p&gt;

&lt;p&gt;I’d wanted to figure out an approach which would either solve or mitigate these
issues and that would be a foundation to build upon. In coming up with this,
I’ve taken a lot from &lt;a href=&quot;https://charity.wtf/tag/terraform/&quot;&gt;Charity Major’s writing&lt;/a&gt;, &lt;a href=&quot;https://terraformbook.com&quot;&gt;The Packer
Book&lt;/a&gt; (which filled in many gaps in my understanding when it came
to how remote state could all fit together) and
&lt;a href=&quot;https://github.com/travis-ci/terraform-config&quot;&gt;Travis CI’s open source configuration&lt;/a&gt;. I recommend
exploring all of these.&lt;/p&gt;

&lt;h2 id=&quot;understanding-remote-state&quot;&gt;Understanding Remote State&lt;/h2&gt;

&lt;p&gt;The solution to some of these problems (splitting environments, collaboration,
etc.) lies in using &lt;a href=&quot;https://www.terraform.io/docs/state/index.html&quot;&gt;remote state&lt;/a&gt;. This wasn’t a feature back when I started
using Terraform but opens up some interesting patterns.&lt;/p&gt;

&lt;p&gt;Terraform uses the state to &lt;a href=&quot;https://www.terraform.io/docs/state/purpose.html&quot;&gt;build a map between your configuration files, the
real-world resources which back them, and how the dependencies slot
together&lt;/a&gt;. By default, this is held locally.&lt;/p&gt;

&lt;p&gt;This works out okay for small projects, but as your configuration and those
trying to collaborate on grow this begins to become unwieldy. You risk
overwriting someone else’s changes or suffering through particularly horrible
merge conflicts.&lt;/p&gt;

&lt;p&gt;Remote state helps mitigate these. The &lt;a href=&quot;https://www.terraform.io/docs/backends&quot;&gt;backend&lt;/a&gt; can provide locking so that
people’s changes don’t override another’s (for example, S3 with a DynamoDB
table or Consul) and data can be referred to across different environments
which brings with it the opportunity for smart separation of concerns when it
comes to Terraform configuration.&lt;/p&gt;

&lt;h2 id=&quot;directory-structure&quot;&gt;Directory Structure&lt;/h2&gt;

&lt;p&gt;Something I’d struggled with when reading other things was understanding the
bigger picture of how everything could fit together, and some limitations
imposed by how Terraform works with directories. Before going over the details
of each directory, here’s the overall directory structure that I’ve been
working with:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;modules/ # shared modules
  remote_state/
    interface.tf
    main.tf
state/ # &quot;environment&quot; for bootstrapping state buckets
  main.tf
  terraform.tfstate
global/ # everything which doesn&apos;t fit inside a specific environment
  main.tf
production/
  main.tf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This focuses on each of the environments, one of which is for handling the
state (and the only one with a local file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.tfstate&lt;/code&gt;), one for things like DNS
or IAM details which don’t fit into a specific environment and then the first
true environment, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;production&lt;/code&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;modules&lt;/code&gt; allow us to share between
environments.&lt;/p&gt;

&lt;p&gt;Terraform doesn’t descend any further than the current directory when looking
for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*.tf&lt;/code&gt; files. This means that it’s not possible to use directories for
grouping (e.g.: you can’t do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;global/dns/main.tf&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;global/iam/main.tf&lt;/code&gt;, for
example). Instead, this encourages us to push configuration into reusable
modules and simplify the depth of the directory tree.&lt;/p&gt;

&lt;p&gt;Depending on needs, it might make sense to annotate regions inside the
environment name, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;europe-production&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;us-production&lt;/code&gt;, etc.&lt;/p&gt;

&lt;h2 id=&quot;workflow&quot;&gt;Workflow&lt;/h2&gt;

&lt;p&gt;Initially, you’ll want to work inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;state&lt;/code&gt; to build up the resources
required for storing state for the rest of your environments. Depending on your
circumstances, you may find this is better implemented independently from the
main Terraform configuration, but I’ve found it’s fine in the same repository.
It might look something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# state/main.tf
provider &quot;aws&quot; {
  version = &quot;~&amp;gt; 1.15&quot;
  region  = &quot;eu-west-1&quot;
}

module &quot;remote_state_global&quot; {
  source = &quot;../modules/remote_state&quot;

  environment = &quot;global&quot;
}

module &quot;remote_state_production&quot; {
  source = &quot;../modules/remote_state&quot;

  environment = &quot;production&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# modules/remote_state/interface.tf
variable &quot;prefix&quot; {
  default     = &quot;ng-test&quot;
  description = &quot;Organisation Name to prefix buckets with&quot;
}

variable &quot;environment&quot; {
  default     = &quot;development&quot;
  description = &quot;Environment Name&quot;
}

output &quot;s3_bucket_id&quot; {
  value = &quot;${aws_s3_bucket.remote_state.id}&quot;
}

# modules/remote_state/main.tf
resource &quot;aws_s3_bucket&quot; &quot;remote_state&quot; {
  bucket = &quot;${var.prefix}-remote-state-${var.environment}&quot;
  acl    = &quot;authenticated-read&quot;

  versioning {
    enabled = true
  }

  tags {
    Name        = &quot;${var.prefix}-remote-state-${var.environment}&quot;
    Environment = &quot;${var.environment}&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After this, you can start building out one of the environments. I found
thinking about the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;global&lt;/code&gt; environment the easiest place to start; it holds
many prerequisites for the other environments, like DNS, S3 buckets and IAM
details. It might look something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# global/main.tf
terraform {
  backend &quot;s3&quot; {
    bucket = &quot;ng-test-remote-state-global&quot;
    key    = &quot;terraform.tfstate&quot;
    region = &quot;eu-west-1&quot;
  }
}

provider &quot;aws&quot; {
  version = &quot;~&amp;gt; 1.15&quot;
  region  = &quot;eu-west-1&quot;
}

resource &quot;aws_s3_bucket&quot; &quot;example&quot; {
  bucket = &quot;ng-test-example&quot;
  acl    = &quot;private&quot;

  tags {
    Name        = &quot;NG: Test Example Bucket&quot;
    Environment = &quot;ng-test&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you start developing infrastructure, patterns will start to emerge. This can
be build up into &lt;a href=&quot;https://www.terraform.io/docs/modules/usage.html&quot;&gt;Terraform Modules&lt;/a&gt; and shared. A &lt;a href=&quot;https://registry.terraform.io&quot;&gt;public registry&lt;/a&gt; exists
which can help speed up understanding what you can do.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Kubernetes with Terraform on Google Cloud</title>
        <link href="https://nickcharlton.net/posts/kubernetes-terraform-google-cloud.html" />
        <id>https://nickcharlton.net/posts/kubernetes-terraform-google-cloud.html</id>
        <published>Mon, 07 May 2018 00:00:00 +0000</published>
        <updated>Mon, 07 May 2018 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I’ve been playing around with &lt;a href=&quot;https://kubernetes.io&quot;&gt;Kubernetes&lt;/a&gt; a bunch recently, especially with
&lt;a href=&quot;https://cloud.google.com/kubernetes-engine/&quot;&gt;Google Kubernetes Engine&lt;/a&gt; (GKE). &lt;a href=&quot;https://cloud.google.com&quot;&gt;Google Cloud&lt;/a&gt;, and especially, their
managed Kubernetes solution works really well. The best tool for configuring
this sort of thing is &lt;a href=&quot;https://terraform.io&quot;&gt;Terraform&lt;/a&gt;, but the few examples I came across had
lots of extra complexity which I felt distracted from what really needed to be
there. This starts with a very basic implementation to bring up a cluster,
through to some useful configuration for nodes which you can build on.&lt;/p&gt;

&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;

&lt;p&gt;Firstly, if you haven’t already, make sure you have an account with &lt;a href=&quot;https://cloud.google.com&quot;&gt;Google
Cloud&lt;/a&gt;, have installed Terraform and the &lt;a href=&quot;https://cloud.google.com/sdk/docs/&quot;&gt;Google Cloud SDK&lt;/a&gt;. Then, there
are some configuration prerequisites to walk through.&lt;/p&gt;

&lt;p&gt;Google Cloud organises resources under a “project”. Here, I’m going to be using
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform-gke&lt;/code&gt; as an example project to work with. You can set this up in the
&lt;a href=&quot;https://console.cloud.google.com/&quot;&gt;Google Cloud Console&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, we need to set up a few things to have access via the API. First,
&lt;a href=&quot;https://console.developers.google.com/apis/api/container.googleapis.com/overview&quot;&gt;enable the GKE API in the Google Developer’s Console&lt;/a&gt;. Then, we’ll
need service account credentials to use the API. In the “Credentials” section,
choose “Create Credentials” and then “Service account key”.&lt;/p&gt;

&lt;p&gt;You should then be asked to select which account to use. If GKE API access is
setup correctly, you’ll see “Compute Engine default service account”. That’ll
do fine for our requirements, so select that and “JSON” as the type.&lt;/p&gt;

&lt;p&gt;Now, create a file for the provider, e.g.: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google.tf&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;provider &quot;google&quot; {
  credentials = &quot;${file(&quot;account.json&quot;)}&quot;
  project     = &quot;terraform-gke&quot;
  region      = &quot;europe-west2&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;account.json&lt;/code&gt; file is what we’d previously downloaded. Setting the project
here allows us to avoid setting it elsewhere, and the same with the &lt;a href=&quot;https://cloud.google.com/appengine/docs/locations&quot;&gt;region&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform init&lt;/code&gt; to have Terraform download the required provider.&lt;/p&gt;

&lt;h2 id=&quot;basic-cluster&quot;&gt;Basic Cluster&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;resource &quot;google_container_cluster&quot; &quot;primary&quot; {
  name               = &quot;gke-example&quot;
  zone               = &quot;europe-west2-a&quot;
  initial_node_count = 2
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is the most basic, but workable cluster I could come up with. It has two
nodes, exists in a single availability zone and uses the smallest standard
&lt;a href=&quot;https://cloud.google.com/compute/docs/machine-types&quot;&gt;machine type&lt;/a&gt; available (which defaults to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;n1-standard-1&lt;/code&gt;). It’s named
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gke-example&lt;/code&gt;. You could place this in the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google.tf&lt;/code&gt; file, but any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.tf&lt;/code&gt;
would do.&lt;/p&gt;

&lt;p&gt;If you run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform apply&lt;/code&gt;, this will bring the cluster up.&lt;/p&gt;

&lt;p&gt;To use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl&lt;/code&gt;, the credentials can be fetched using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gcloud&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gcloud config &lt;span class=&quot;nb&quot;&gt;set &lt;/span&gt;project terraform-gke
gcloud container clusters get-credentials gke-example
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…which sets the correct project, and then fetches the credentials for our
cluster. Now, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl cluster-info&lt;/code&gt; should tell you something about the
cluster which was just created.&lt;/p&gt;

&lt;h2 id=&quot;managing-the-default-node-pool&quot;&gt;Managing the Default Node Pool&lt;/h2&gt;

&lt;p&gt;GKE uses the idea of “node pools” to segregate specific types of nodes in a
cluster. You might have a set of default size nodes which are applicable to
most job requirements, but some high-memory nodes for larger batch jobs or
perhaps some with attached GPUs for some machine learning work.&lt;/p&gt;

&lt;p&gt;By default, creating a cluster (either through Terraform, or manually with the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gcloud&lt;/code&gt; client will create a default node pool which you’re not able to manage
yourself. This means that if you want to enable auto-scaling, custom machine
types or any of the management options, this isn’t available to you through
Terraform.&lt;/p&gt;

&lt;p&gt;The solution to this is to delete the initial node pool and provide our own.&lt;/p&gt;

&lt;p&gt;We can do this like so:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;resource &quot;google_container_cluster&quot; &quot;primary&quot; {
  name                     = &quot;gke-example&quot;
  zone                     = &quot;europe-west2-a&quot;
  remove_default_node_pool = true

  node_pool {
    name = &quot;default-pool&quot;
  }
}

resource &quot;google_container_node_pool&quot; &quot;primary_pool&quot; {
  name       = &quot;primary-pool&quot;
  cluster    = &quot;${google_container_cluster.primary.name}&quot;
  zone       = &quot;europe-west2-a&quot;
  node_count = &quot;2&quot;

  node_config {
    machine_type = &quot;n1-standard-1&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We need to either provide an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initial_node_count&lt;/code&gt; or a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node_pool&lt;/code&gt; block, so
here we just name the default node pool. This is the node which will be removed
after initial cluster creation. We then go onto configuring the “primary pool”
with two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;n1-standard-1&lt;/code&gt; nodes.&lt;/p&gt;

&lt;h2 id=&quot;improving-the-initial-nodes&quot;&gt;Improving the Initial Nodes&lt;/h2&gt;

&lt;p&gt;One of the great things about running on a managed Kubernetes provider is
having the sort of the upgrade and repair functionality you’d only get with a
legion of staff in a traditional hosting environment.&lt;/p&gt;

&lt;p&gt;For &lt;a href=&quot;https://cloud.google.com/kubernetes-engine/docs/concepts/node-auto-upgrades&quot;&gt;auto-upgrades&lt;/a&gt;, this helps ensure you’re running a recent stable build
of Kubernetes across your cluster and for [auto-repair][], it ensures that
every node in the cluster is functioning correctly. If a node is behaving
incorrectly, the provisioned Pods will be migrated off and the node replaced
with a new one.&lt;/p&gt;

&lt;p&gt;These two features can be enabled with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;management&lt;/code&gt; block inside the
node pool:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;management {
  auto_repair  = true
  auto_upgrade = true
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;GKE also supports another great feature: Auto-scaling. If the required load on
the cluster can’t be handled by the amount of currently provisioned nodes,
GKE will spin up more nodes (up to a limit). In the reverse, if there’s ample
capacity on the cluster, GKE will remove nodes from it.&lt;/p&gt;

&lt;p&gt;Much like the management functionality, this can be enabled inside the node
pool block:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;autoscaling {
  min_node_count = 2
  max_node_count = 5
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Whilst this is something that’ll depend much on your use case, it seems wise to
keep the auto-scaling max just above the peak load you’d expect to see in
normal circumstances. But, of course, your mileage will vary.&lt;/p&gt;

&lt;h2 id=&quot;additional-node-pools--preemptable-nodes&quot;&gt;Additional Node Pools &amp;amp; Preemptable Nodes&lt;/h2&gt;

&lt;p&gt;Not all of your workloads might warrant the same set of underlying nodes. If
you have a few background jobs which require much more memory but otherwise
have a workload which is primarily web requests, you might save money by adding
an additional node pool for the background jobs that are configured with a
different machine type.&lt;/p&gt;

&lt;p&gt;Some of your workloads might be fault-tolerant enough to use &lt;a href=&quot;https://cloud.google.com/kubernetes-engine/docs/concepts/preemptible-vm&quot;&gt;Preemptable
VMs&lt;/a&gt;, which are priced significantly cheaper than normal instances but only
exist for 24hrs (before being re-provisioned) and might be terminated on
short notice.&lt;/p&gt;

&lt;p&gt;You can use &lt;a href=&quot;https://cloud.google.com/kubernetes-engine/docs/how-to/node-taints&quot;&gt;node taints inside Kubernetes&lt;/a&gt; to make sure the right
jobs right on the right nodes. In Terraform, these are configured just like our
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;primary-pool&lt;/code&gt; above.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Book Review: How To Have Your Cake And Eat It Too: An Introduction to Service Design</title>
        <link href="https://nickcharlton.net/posts/book-review-introduction-to-service-design.html" />
        <id>https://nickcharlton.net/posts/book-review-introduction-to-service-design.html</id>
        <published>Sat, 03 Mar 2018 00:00:00 +0000</published>
        <updated>Sat, 03 Mar 2018 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Whilst I do spend most of my time writing code, I’m also a consultant. Every
year I work with a bunch of small companies, founders and those who are running
teams in much larger organisations. These are people who &lt;em&gt;do&lt;/em&gt; care about the
quality of the code I might produce, how well tested it is and that decisions
have been made for the long term, but that’s all a bit moot if we’re not
building the right thing for their customers.&lt;/p&gt;

&lt;p&gt;Over the last few years, government and large enterprises have gradually
started to come around to what’s made new upstarts so successful: focusing on
the user and their needs.&lt;/p&gt;

&lt;p&gt;For the new upstarts, some of this has been easy. They don’t have decades worth
of processes and organisational structure which stands in the way of delivering
on their goals. For successful new companies, they can be arranged from the
customers’ perspectives from the start. If they’re not, they’re young and small
enough to iterate towards this quickly (or simply just not make it). For
existing organisations, this is rarely the case.&lt;/p&gt;

&lt;p&gt;Service Design is a collection of tools which allow you to find out what
customers actually value in a given service or product.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Who are your customers?&lt;/li&gt;
  &lt;li&gt;What journey does a customer take in using your service?&lt;/li&gt;
  &lt;li&gt;What are they trying to achieve?&lt;/li&gt;
  &lt;li&gt;What are their pain points?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These can be answered by building personas, drawing out customer journey maps
and then doing customer interviews to fill in the gaps.&lt;/p&gt;

&lt;p&gt;The consultant is a product of the tools in their toolbox. I’ve participated in
several, and reasonably recently run a design sprint. I’ve built products using
various flavours (and in various levels of dysfunction) of agile. But I’d
been hunting for something out of those which is missing, and a step into this
“service design” thing I’d been hearing about regularly.&lt;/p&gt;

&lt;p&gt;J. Margus Klaar presents us with a brief overview of all of the above. At 106
pages, it’s short enough to read and digest in a couple of hours and present
you with another collection of tools to have at your disposal.&lt;/p&gt;

&lt;p&gt;They discuss how consumer actions have changed over the past fifty years and
how in reaction to the world changing, people’s behaviour has changed. They
discuss cases where looking at customer pain points have driven innovative
solutions (like Apple and short mouse cables because they put a USB hub in the
keyboard) and cases where customer relationships have completely changed from
where the organisation once believed they were (e.g.: young people and not
caring about their banks).&lt;/p&gt;

&lt;p&gt;Then they go on to outline the common tools for delivering this customer focus.
User journeys, personas, empathy maps, interviews, touch-points.&lt;/p&gt;

&lt;p&gt;The book covers these quite well but doesn’t go into how to use them in detail
or run them in a session or workshop. For example, a good chunk of the book
discusses talking to users but seems to avoid providing example questions
someone might ask. This is probably partly a requirement (the nature of those
questions would emerge from the process) and also helpful in not prescribing
how to use them.&lt;/p&gt;

&lt;p&gt;One of the most important parts of this process is that it is visual and these
are shown with a collection of illustrations. I can see myself copying many of
them out onto a whiteboard in the future…&lt;/p&gt;

&lt;p&gt;Broadly, I found the examples easy to understand and extract something from.
But I would have liked to see some more in-depth case studies showing how
changes had successfully been bought out using these tools.&lt;/p&gt;

&lt;p&gt;If you’re looking for one resource to point to, that covers the basics and is
concise, I’d recommend this.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.amazon.co.uk/dp/9063693818/ref=as_li_ss_tl?ie=UTF8&amp;amp;linkCode=ll1&amp;amp;tag=nisbl-21&amp;amp;linkId=41b826ae83227eff6db38826139cbbd7&quot;&gt;You can find it on Amazon&lt;/a&gt;.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Setting up CircleCI 2.0 for Rails</title>
        <link href="https://nickcharlton.net/posts/circleci-2-rails.html" />
        <id>https://nickcharlton.net/posts/circleci-2-rails.html</id>
        <published>Wed, 03 Jan 2018 00:00:00 +0000</published>
        <updated>Wed, 03 Jan 2018 00:00:00 +0000</updated>
        <summary type="html">&lt;blockquote&gt;
  &lt;p&gt;CircleCI 2.0 brings some great new features, but it’s not so straightforward to get started with. Let’s walk through how to get going.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://thoughtbot.com/blog/circleci-2-rails&quot;&gt;See on Giant Robots&lt;/a&gt;&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>2017</title>
        <link href="https://nickcharlton.net/posts/2017.html" />
        <id>https://nickcharlton.net/posts/2017.html</id>
        <published>Tue, 02 Jan 2018 00:00:00 +0000</published>
        <updated>Tue, 02 Jan 2018 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;2017 was a tough one.&lt;/p&gt;

&lt;p&gt;This year was the start of the implementation of the bad things which came
about in 2016. Brexit, Trump and a further, significant, continuation of the
expansion of social, racial and economic inequality. Many of these things go
back a very long time and so if you’re surprised by them, you’ve not been
paying enough attention.&lt;/p&gt;

&lt;p&gt;For me, 2017 was exhausting and stressful. But also a time where I got to the
stage where I started finding myself and &lt;em&gt;doing&lt;/em&gt; the sorts of things I’ve been
wanting to do.&lt;/p&gt;

&lt;p&gt;At the end of 2016, I took over being the maintainer of &lt;a href=&quot;https://github.com/thoughtbot/administrate&quot;&gt;administrate&lt;/a&gt;, a
popular open source project for building admin dashboards with Rails. It’d been
abandoned for much of 2016 and seeing a shitty issue irritated me enough to
take over maintaining it.&lt;/p&gt;

&lt;p&gt;I started with a big issue and pull request backlog and since this has been a
relentless stream of work. With the help of my some of my amazing colleagues,
&lt;a href=&quot;https://www.tysongach.com&quot;&gt;Tyson Gach&lt;/a&gt; , &lt;a href=&quot;http://www.pablobm.com&quot;&gt;Pablo Brasero&lt;/a&gt; (to highlight two) and a wonderful
collection of external contributors (&lt;a href=&quot;https://github.com/jayroh&quot;&gt;Joel&lt;/a&gt;, &lt;a href=&quot;https://github.com/carlosramireziii&quot;&gt;Carlos&lt;/a&gt;, &lt;a href=&quot;https://github.com/BenMorganIO&quot;&gt;Ben&lt;/a&gt;, &lt;a href=&quot;https://github.com/pedantic-git&quot;&gt;Rich&lt;/a&gt; 
and many more) we’ve been able to remove a large number of dependencies 
(including by implementing our own design/grid system), add a much-wanted 
feature in namespace support, kept track of Rails releases and have worked hard 
on our handling of internationalisation.&lt;/p&gt;

&lt;p&gt;I was able to help get &lt;a href=&quot;https://todo-london.com&quot;&gt;// TODO London&lt;/a&gt; started by providing their
initial event space (something we’ll be doing in 2018, too). This is
possibly the best London meetup and I’m very happy that this worked out so
well.&lt;/p&gt;

&lt;p&gt;At &lt;a href=&quot;https://thoughtbot.com/london&quot;&gt;thoughtbot&lt;/a&gt;, we’ve run three events of our own. &lt;a href=&quot;https://www.youtube.com/watch?v=3LiY4uhuTeQ&quot;&gt;One about how
developers grow&lt;/a&gt;, &lt;a href=&quot;https://www.eventbrite.co.uk/e/panel-discussion-design-sprints-tickets-36773548674#&quot;&gt;one about design
sprints&lt;/a&gt;, and finally &lt;a href=&quot;http://giantrobots.fm/256&quot;&gt;about our company
playbook&lt;/a&gt;. I helped with some of the filming of those
events, and along with &lt;a href=&quot;https://nootch.net&quot;&gt;Bruno Antunes&lt;/a&gt; and &lt;a href=&quot;http://www.interroban.gg&quot;&gt;Luke Mitchell&lt;/a&gt; put
together a significantly updated version of our playbook …and got it printed
into a real book!&lt;/p&gt;

&lt;p&gt;I started co-hosting &lt;a href=&quot;/posts/co-hosting-build-phase-podcast.html&quot;&gt;Build Phase&lt;/a&gt;, a podcast about iOS development (a
thing I once did a lot of, and now sadly do a lot less) …but then
successfully allowed that to tail off completely. I also joined &lt;a href=&quot;http://chadpytel.com/&quot;&gt;Chad
Pytel&lt;/a&gt; for an episode of &lt;a href=&quot;/posts/giant-robots-podcast-wwdc.html&quot;&gt;Giant Robots&lt;/a&gt; as a reflection of WWDC
2017.&lt;/p&gt;

&lt;p&gt;I was able to expand my interest in &lt;a href=&quot;https://kubernetes.io&quot;&gt;Kubernetes&lt;/a&gt;, &lt;a href=&quot;https://golang.org&quot;&gt;Go&lt;/a&gt;,
&lt;a href=&quot;https://www.docker.com&quot;&gt;Docker&lt;/a&gt; and virtualisation, all of which I’m planning to write about in
2018.&lt;/p&gt;

&lt;p&gt;Somewhere in here, I also managed to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;start brewing (full grain) beer and found my favourite recipe which
produces a not-too-heavy dark beer (much like a mild),&lt;/li&gt;
  &lt;li&gt;view the 2017 eclipse in Wyoming with two great friends, followed by
road-tripping from Colorado to San Fransisco …then getting robbed when
our car was broken into,&lt;/li&gt;
  &lt;li&gt;got to go down an abandoned part of the tube and an old air-raid shelter,&lt;/li&gt;
  &lt;li&gt;moved house from North London to Docklands sort-of-area with three friends
who somehow are capable of putting up with me,&lt;/li&gt;
  &lt;li&gt;and probably reading the largest amount of books I ever have before.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As I began writing this, I was unsure of what 2017 had been (apart from seeing
it as a struggle). But as is often the case with reflecting on a year, it turns
out to have a lot more in there than you’d realised.&lt;/p&gt;

&lt;p&gt;Sadly, I end 2017 only-mildly avoiding a serious burnout. If it’s not the
burden of maintaining open-source projects (both those through work and those
of my own), trying to assist with sales and marketing efforts with work, or
just work in general, it’s been a tough one.&lt;/p&gt;

&lt;p&gt;For 2018, I’m focusing on &lt;em&gt;documenting and self-management&lt;/em&gt;. For the former: a
combination of writing and at least one meetup talk. For the latter, aiming to
manage my time and well-being better to try and avoid burning out.&lt;/p&gt;

&lt;p&gt;I want 2018 to be a year where I communicate better and mentor more.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>On Giant Robots for WWDC</title>
        <link href="https://nickcharlton.net/posts/giant-robots-podcast-wwdc.html" />
        <id>https://nickcharlton.net/posts/giant-robots-podcast-wwdc.html</id>
        <published>Wed, 07 Jun 2017 00:00:00 +0000</published>
        <updated>Wed, 07 Jun 2017 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;If &lt;a href=&quot;/posts/co-hosting-build-phase-podcast.html&quot;&gt;starting to co-host Build Phase&lt;/a&gt; wasn’t enough, &lt;a href=&quot;http://chadpytel.com&quot;&gt;Chad Pytel&lt;/a&gt; and
I did a &lt;a href=&quot;https://developer.apple.com/wwdc/&quot;&gt;WWDC&lt;/a&gt; cross-over episode of &lt;a href=&quot;http://giantrobots.fm/&quot;&gt;Giant Robots&lt;/a&gt;, the main company
podcast.&lt;/p&gt;

&lt;p&gt;We ran through the news out of the &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2017/101/&quot;&gt;main keynote&lt;/a&gt; and parts of the &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2017/102/&quot;&gt;Platforms
State of the Union&lt;/a&gt; (the developer keynote) and what we thought about in just
about an hour. It was good fun and a bit different to talk about something
which is all tech news. I hope we’ll do it again!&lt;/p&gt;

&lt;iframe frameborder=&quot;0&quot; height=&quot;330px&quot; scrolling=&quot;no&quot; seamless=&quot;&quot; src=&quot;https://simplecast.com/e/72374?style=large&quot; width=&quot;100%&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;You can find future episodes of &lt;a href=&quot;http://giantrobots.fm/&quot;&gt;Giant Robots&lt;/a&gt; at
&lt;a href=&quot;http://giantrobots.fm/&quot;&gt;giantrobots.fm&lt;/a&gt;, on &lt;a href=&quot;https://itunes.apple.com/us/podcast/giant-robots-smashing-into/id535121941&quot;&gt;iTunes&lt;/a&gt; or where-ever you like to listen
to podcasts.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Co-hosting Build Phase</title>
        <link href="https://nickcharlton.net/posts/co-hosting-build-phase-podcast.html" />
        <id>https://nickcharlton.net/posts/co-hosting-build-phase-podcast.html</id>
        <published>Wed, 07 Jun 2017 00:00:00 +0000</published>
        <updated>Wed, 07 Jun 2017 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I now co-host a podcast!&lt;/p&gt;

&lt;p&gt;A few weeks ago, I agreed to take over co-hosting &lt;a href=&quot;http://buildphase.fm&quot;&gt;Build Phase&lt;/a&gt;, our podcast
about mobile (mostly iOS) development. I’m joining &lt;a href=&quot;http://www.nuthole.com&quot;&gt;Jack Nutting&lt;/a&gt;, who used
to work with us in Stockholm.&lt;/p&gt;

&lt;p&gt;Podcasting is something I’ve wanted to do for a long time, but without someone
prompting me, I was going to keep avoiding it. Fortunately, Jack knows what
he’s doing and helped make the first episode pretty painless. We’ll be
releasing new episodes every other week from here on out.&lt;/p&gt;

&lt;p&gt;In the first episode, I talk a little about my first day at &lt;a href=&quot;https://thoughtbot.com&quot;&gt;thoughtbot&lt;/a&gt;
(which was New Bamboo at the time … but not for very much longer), building
&lt;a href=&quot;https://en.wikipedia.org/wiki/Hypermedia&quot;&gt;Hypermedia&lt;/a&gt; APIs, managing client expectations, a bit about &lt;a href=&quot;https://github.com/apple/swift-evolution&quot;&gt;Swift
evolution&lt;/a&gt;, and how we’re still having to deal with &lt;a href=&quot;https://en.wikipedia.org/wiki/File_Transfer_Protocol&quot;&gt;FTP&lt;/a&gt;.&lt;/p&gt;

&lt;iframe frameborder=&quot;0&quot; height=&quot;330px&quot; scrolling=&quot;no&quot; seamless=&quot;&quot; src=&quot;https://simplecast.com/e/71738?style=large&quot; width=&quot;100%&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;You can find future episodes at &lt;a href=&quot;http://buildphase.fm&quot;&gt;buildphase.fm&lt;/a&gt;, on Twitter as
&lt;a href=&quot;https://twitter.com/buildphase&quot;&gt;@buildphase&lt;/a&gt;, on &lt;a href=&quot;https://itunes.apple.com/us/podcast/build-phase/id681232605&quot;&gt;iTunes&lt;/a&gt; or where-ever you like to listen to podcasts.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Automating macOS using Ansible</title>
        <link href="https://nickcharlton.net/posts/automating-macos-using-ansible.html" />
        <id>https://nickcharlton.net/posts/automating-macos-using-ansible.html</id>
        <published>Tue, 06 Dec 2016 00:00:00 +0000</published>
        <updated>Tue, 06 Dec 2016 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I have a collection of macOS machines, some of which are used for services like
&lt;a href=&quot;/posts/installing-jenkins-osx-yosemite.html&quot;&gt;Jenkins&lt;/a&gt; and others which are used for testing. These all have a consistent
base configuration which I’ve been using Ansible to configure.&lt;/p&gt;

&lt;p&gt;Ansible is being used to to drive a set of utilities that have emerged to help
with some of the rougher edges of configuring macOS. In places where this
abstraction isn’t available, I’m calling out to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;defaults write&lt;/code&gt; to set
settings directly. There’s a &lt;a href=&quot;https://github.com/nickcharlton/dotfiles/blob/master/osx/defaults.sh&quot;&gt;few&lt;/a&gt; &lt;a href=&quot;https://github.com/mathiasbynens/dotfiles/blob/master/.macos&quot;&gt;examples&lt;/a&gt; and
a &lt;a href=&quot;http://www.defaults-write.com&quot;&gt;site dedicated&lt;/a&gt; to figuring out what’s possible.&lt;/p&gt;

&lt;p&gt;I’m usually doing this in VMs, so I start by creating a new one using a set of
&lt;a href=&quot;https://github.com/nickcharlton/packer-osx&quot;&gt;Packer templates&lt;/a&gt; which create a base machine using a recent installer, runs
updates and installs an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh&lt;/code&gt; key. From here, I’ll then run an Ansible playbook
that will:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Install &lt;a href=&quot;https://github.com/homebrew/brew&quot;&gt;Homebrew&lt;/a&gt;,&lt;/li&gt;
  &lt;li&gt;Set the dock position, size and contents,&lt;/li&gt;
  &lt;li&gt;Show devices on the desktop, align files to the grid, show hidden files, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It ends up looking like this:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/ansible_configured_macos_vm.png&quot; alt=&quot;Ansible Configured macOS VM&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Ansible Configured macOS VM&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Some of these settings are the same on my own Macs, but most of these (like the
dock and it’s contents) are all about making it as easy as possible to interact
with it over a screen sharing session and having something consistent across
the board.&lt;/p&gt;

&lt;p&gt;To achieve these, I do this:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Base OS X configuration&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;hosts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;all&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;geerlingguy.homebrew&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;base&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is the base playbook. It’s held in a central &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ansible&lt;/code&gt; repo, but here
we’re installing Homebrew (using &lt;a href=&quot;https://github.com/geerlingguy/ansible-role-homebrew&quot;&gt;Jeff Geerling’s Homebrew Role&lt;/a&gt;) and
then executing the base role. The base role looks like this:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Install base utilities&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;homebrew&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;}}&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;with_items&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;m-cli&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dockutil&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Remove all items from the Dock&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/usr/local/bin/dockutil --remove all&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Set the default Dock items&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/usr/local/bin/dockutil&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;--add&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;}}&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;--no-restart&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;with_items&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/Applications/Safari.app&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/Applications/App&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Store.app&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/Applications/System&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Preferences.app&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/Applications/Utilities/Terminal.app&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/Applications/Utilities/Console.app&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/Applications&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;--section&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;others&quot;&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;~/Downloads&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;--section&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;others&quot;&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Reduce the size of the Dock to 30 points&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;defaults write com.apple.dock tilesize -int &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;30&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Show the Dock on the left-hand side&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/usr/local/bin/m dock position LEFT&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Disable the Screensaver&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;defaults write com.apple.screensaver idleTime &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Arrange Files by Kind&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;/usr/libexec/PlistBuddy -c &quot;Set :DesktopViewSettings:IconViewSettings:arrangeBy kind&quot; ~/Library/Preferences/com.apple.finder.plist&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;/usr/libexec/PlistBuddy -c &quot;Set :StandardViewSettings:IconViewSettings:arrangeBy kind&quot; ~/Library/Preferences/com.apple.finder.plist&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Set the Grid Spacing for Files&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;/usr/libexec/PlistBuddy -c &quot;Set :DesktopViewSettings:IconViewSettings:gridSpacing 54&quot; ~/Library/Preferences/com.apple.finder.plist&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;/usr/libexec/PlistBuddy -c &quot;Set :StandardViewSettings:IconViewSettings:gridSpacing 30&quot; ~/Library/Preferences/com.apple.finder.plist&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Use Smaller Icons&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;/usr/libexec/PlistBuddy -c &quot;Set :DesktopViewSettings:IconViewSettings:iconSize 48&quot; ~/Library/Preferences/com.apple.finder.plist&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;/usr/libexec/PlistBuddy -c &quot;Set :StandardViewSettings:IconViewSettings:iconSize 64&quot; ~/Library/Preferences/com.apple.finder.plist&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Show ~/Library&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;chflags nohidden ~/Library&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Show Drives on the Desktop&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;defaults write com.apple.finder ShowHardDrivesOnDesktop -bool &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Show External Drives on the Desktop&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;defaults write com.apple.finder ShowExternalHardDrivesOnDesktop -bool &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Show Removable Media on the Desktop&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;defaults write com.apple.finder ShowRemovableMediaOnDesktop -bool &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Show Hidden Files&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;defaults write com.apple.finder AppleShowAllFiles -bool &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Show File Extensions&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;defaults write NSGlobalDomain AppleShowAllExtensions -bool &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Show the Status Bar in Finder&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;defaults write com.apple.finder ShowStatusBar -bool &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Show the Path Bar in Finder&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;defaults write com.apple.finder ShowPathbar -bool &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Restart Finder&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;killall Finder&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It relies on &lt;a href=&quot;https://github.com/rgcr/m-cli&quot;&gt;m-cli&lt;/a&gt; and &lt;a href=&quot;https://github.com/kcrawford/dockutil&quot;&gt;dockutil&lt;/a&gt; to reconfigure the dock and then relies
on an Ansible form of some of my existing &lt;a href=&quot;https://github.com/nickcharlton/dotfiles&quot;&gt;dotfiles&lt;/a&gt; &lt;a href=&quot;https://github.com/nickcharlton/dotfiles/blob/master/osx/defaults.sh&quot;&gt;defaults.sh&lt;/a&gt; script.
These commands need to be run inside a shell, so they’re executed through
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shell&lt;/code&gt; (only through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;command&lt;/code&gt; will cause them to fail).&lt;/p&gt;

&lt;p&gt;Of note, I found that I now need to set the ordering of Finder items and their
size before showing additional devices. This wasn’t the case before OS X 10.11.
(We need to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PlistBuddy&lt;/code&gt; because it’s nested, but this doesn’t seem to be
the reason for why.)&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Building Virtual Machines with Packer on ESXi 6</title>
        <link href="https://nickcharlton.net/posts/using-packer-esxi-6.html" />
        <id>https://nickcharlton.net/posts/using-packer-esxi-6.html</id>
        <published>Sat, 03 Dec 2016 00:00:00 +0000</published>
        <updated>Sat, 03 Dec 2016 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Over the past couple of weeks, I’ve been building up a box running ESXi 6 to
host a bunch of virtual machines. I &lt;a href=&quot;https://nickcharlton.net/posts/configuring-esxi-6-on-hetzner.html&quot;&gt;documented the initial configuration in a
previous post&lt;/a&gt;, but this goes further and attempts to automate
much of this using &lt;a href=&quot;https://packer.io&quot;&gt;Packer&lt;/a&gt;. My end goal is to have the complete
configuration held in a repository to make rebuilding (or adding new boxes) as
painless as possible and also to provide an example for others implementing
something similar.&lt;/p&gt;

&lt;p&gt;I’ve put together a &lt;a href=&quot;https://github.com/nickcharlton/packer-esxi&quot;&gt;(public) repository&lt;/a&gt; which contains templates for
set of base images which can be cloned to speed up provisioning new VMs. These 
templates assume you’re able to acquire an IP via DHCP.&lt;/p&gt;

&lt;h2 id=&quot;configuring-the-esxi-host&quot;&gt;Configuring the ESXi Host&lt;/h2&gt;

&lt;p&gt;Our ESXi host needs a little bit of configuration to allow Packer to work.
Packer communicates over SSH, so first we need to open that. Secondly, we’ll
enable an option to discover Guest IPs from the Host and then finally allow VNC
connections remotely.&lt;/p&gt;

&lt;h3 id=&quot;enable-ssh&quot;&gt;Enable SSH&lt;/h3&gt;

&lt;p&gt;Inside the web UI, navigate to “Manage”, then the “Services” tab. Find the
entry called: “TSM-SSH”, and enable it.&lt;/p&gt;

&lt;p&gt;You may wish to enable it to start up with the host by default. You can do this
inside the “Actions” dropdown (it’s nested inside “Policy”).&lt;/p&gt;

&lt;h3 id=&quot;enable-guest-ip-hack&quot;&gt;Enable “Guest IP Hack”&lt;/h3&gt;

&lt;p&gt;Run the following command on the ESXi host:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;esxcli system settings advanced &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; /Net/GuestIPHack &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This allows Packer to infer the guest IP from ESXi, without the VM needing to
report it itself.&lt;/p&gt;

&lt;h3 id=&quot;open-vnc-ports-on-the-firewall&quot;&gt;Open VNC Ports on the Firewall&lt;/h3&gt;

&lt;p&gt;Packer connects to the VM using VNC, so we’ll &lt;a href=&quot;https://kb.vmware.com/selfservice/microsites/search.do?language=en_US&amp;amp;cmd=displayKC&amp;amp;externalId=2008226&quot;&gt;open a range of
ports&lt;/a&gt; to allow it to connect to it.&lt;/p&gt;

&lt;p&gt;First, ensure we can edit the firewall configuration:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;644 /etc/vmware/firewall/service.xml
&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; +t /etc/vmware/firewall/service.xml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then append the range we want to open to the end of the file:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;service&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1000&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;id&amp;gt;&lt;/span&gt;packer-vnc&lt;span class=&quot;nt&quot;&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;rule&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0000&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;direction&amp;gt;&lt;/span&gt;inbound&lt;span class=&quot;nt&quot;&gt;&amp;lt;/direction&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;protocol&amp;gt;&lt;/span&gt;tcp&lt;span class=&quot;nt&quot;&gt;&amp;lt;/protocol&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;porttype&amp;gt;&lt;/span&gt;dst&lt;span class=&quot;nt&quot;&gt;&amp;lt;/porttype&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;port&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;begin&amp;gt;&lt;/span&gt;5900&lt;span class=&quot;nt&quot;&gt;&amp;lt;/begin&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;end&amp;gt;&lt;/span&gt;6000&lt;span class=&quot;nt&quot;&gt;&amp;lt;/end&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/port&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/rule&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;enabled&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/enabled&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;required&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/required&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/service&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, restore the permissions and reload the firewall:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;444 /etc/vmware/firewall/service.xml
esxcli network firewall refresh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you’d like to, these changes can be persisted between reboots by modifying
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/rc.local.d/local.sh&lt;/code&gt;, &lt;a href=&quot;https://kb.vmware.com/s/article/2043564&quot;&gt;as described in this VMware knowledge base
article&lt;/a&gt;. (&lt;a href=&quot;https://twitter.com/jptoto/status/1262485637132943365?s=12&quot;&gt;Thanks to JP Toto who pointed this
out!&lt;/a&gt;)&lt;/p&gt;

&lt;h2 id=&quot;running-packer&quot;&gt;Running Packer&lt;/h2&gt;

&lt;p&gt;A Packer template for running on ESXi is very similar to one which runs
locally, but we’re not able to use the built-in HTTP server and we specify the
host on which it runs. Here’s an example using Ubuntu 16.04, with a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;preseed.cfg&lt;/code&gt; provided by mounting a floppy:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;variables.json&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;esxi_host&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;esxi_datastore&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;primary&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;esxi_username&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;esxi_password&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ubuntu-1604-base.json&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;builders&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ubuntu-1604-base&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;vm_name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ubuntu-1604-base&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;vmware-iso&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;guest_os_type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ubuntu-64&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;tools_upload_flavor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;linux&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;headless&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;iso_url&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://releases.ubuntu.com/xenial/ubuntu-16.04-server-amd64.iso&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;iso_checksum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;b8b172cbdf04f5ff8adc8c2c1b4007ccf66f00fc6a324a6da6eba67de71746f6&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;iso_checksum_type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;sha256&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ssh_username&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;nullgrid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ssh_password&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;nullgrid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ssh_timeout&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;15m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;disk_type_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;thin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;floppy_files&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;preseed/ubuntu.cfg&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;boot_command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;enter&amp;gt;&amp;lt;wait&amp;gt;&amp;lt;f6&amp;gt;&amp;lt;esc&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&amp;lt;bs&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/install/vmlinuz noapic &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;preseed/file=/floppy/ubuntu.cfg &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;debian-installer=en_US auto locale=en_US kbd-chooser/method=us &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;hostname={{ .Name }} &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;fb=false debconf/frontend=noninteractive &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;keyboard-configuration/modelcode=SKIP keyboard-configuration/layout=USA &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;keyboard-configuration/variant=USA console-setup/ask_detect=false &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;grub-installer/bootdev=/dev/sda &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;initrd=/install/initrd.gz -- &amp;lt;enter&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;shutdown_command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;echo &apos;shutdown -P now&apos; &amp;gt; shutdown.sh; echo &apos;nullgrid&apos;|sudo -S sh &apos;shutdown.sh&apos;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;remote_type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;esx5&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;remote_host&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;{{user `esxi_host`}}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;remote_datastore&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;{{user `esxi_datastore`}}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;remote_username&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;{{user `esxi_username`}}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;remote_password&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;{{user `esxi_password`}}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;keep_registered&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;vmx_data&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ethernet0.networkName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;VM Network&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;preseed.cfg&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-conf highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#
# Based upon: https://help.ubuntu.com/12.04/installation-guide/example-preseed.txt
#
&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# localisation
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;debian&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;installer&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;locale&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;en_US&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;utf8&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;console&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;setup&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;ask_detect&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keyboard&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;layoutcode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;us&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# networking
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;netcfg&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;choose_interface&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;auto&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;netcfg&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;get_hostname&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unassigned&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;hostname&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;netcfg&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;get_domain&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unassigned&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;domain&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;netcfg&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;wireless_wep&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# apt mirrors
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mirror&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;country&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manual&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mirror&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;hostname&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;ubuntu&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mirror&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;directory&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; /&lt;span class=&quot;n&quot;&gt;ubuntu&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mirror&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;proxy&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# clock and time zone
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clock&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;setup&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;utc&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;zone&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GMT&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clock&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;setup&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;ntp&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# partitioning
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;partman&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;auto&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lvm&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;partman&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;lvm&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;device_remove_lvm&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;partman&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;md&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;device_remove_md&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;partman&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;lvm&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;confirm&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# fix: http://serverfault.com/questions/189328/ubuntu-kickstart-installation-using-lvm-waits-for-input
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;partman&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;lvm&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;confirm_nooverwrite&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;partman&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;auto&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;lvm&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;guided_size&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;partman&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;auto&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;choose_recipe&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;atomic&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;partman&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;partitioning&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;confirm_write_new_label&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;partman&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;choose_partition&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;finish&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;partman&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;confirm&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;partman&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;confirm_nooverwrite&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# users
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;passwd&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;fullname&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Null&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Grid&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;passwd&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nullgrid&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;passwd&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nullgrid&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;passwd&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;again&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nullgrid&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;setup&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;allow&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;weak&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;setup&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;encrypt&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;home&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# packages
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tasksel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tasksel&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;multiselect&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;standard&lt;/span&gt;, &lt;span class=&quot;n&quot;&gt;ubuntu&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pkgsel&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;install&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;language&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;support&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pkgsel&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;openssh&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nfs&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;common&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;core&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pkgsel&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;upgrade&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;full&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;upgrade&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pkgsel&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;update&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;policy&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;none&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;postfix&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;postfix&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;main_mailer_type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;No&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# boot loader
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grub&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;installer&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;only_debian&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# hide the shutdown notice
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;finish&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;install&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;reboot_in_progress&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;note&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It can be run like so:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;packer build &lt;span class=&quot;nt&quot;&gt;-var-file&lt;/span&gt; variables.json ubuntu-1604-base.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/nickcharlton/packer-esxi&quot;&gt;packer-esxi&lt;/a&gt; repo contains a much more comprehensive example, which
I’d suggest cloning and using. The Debian example foregoes the floppy and
instead fetches a file from &lt;a href=&quot;http://aws.amazon.com/s3&quot;&gt;Amazon S3&lt;/a&gt; as the installer no longer supports
fetching the preseed from a floppy.&lt;/p&gt;

&lt;p&gt;I’m using these templates to provide a base image, which I clone and configure
to their needs. This is pretty similar to cloud providers like &lt;a href=&quot;https://aws.amazon.com/ec2/&quot;&gt;AWS EC2&lt;/a&gt; or
&lt;a href=&quot;https://m.do.co/c/6ff4dddb5e9d&quot;&gt;Digital Ocean&lt;/a&gt;.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Persistent SSH Keys with ESXi 6</title>
        <link href="https://nickcharlton.net/posts/persistent-ssh-keys-esxi.html" />
        <id>https://nickcharlton.net/posts/persistent-ssh-keys-esxi.html</id>
        <published>Wed, 30 Nov 2016 00:00:00 +0000</published>
        <updated>Wed, 30 Nov 2016 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;ESXi is a funny beast when it comes to SSH keys, and there’s a lot of
misinformation about on how to configure them persistently, not to mention
security FUD.&lt;/p&gt;

&lt;p&gt;ESXi uses an in-memory only filesystem for the bootable portion of it, so
following the &lt;a href=&quot;https://kb.vmware.com/selfservice/microsites/search.do?language=en_US&amp;amp;cmd=displayKC&amp;amp;externalId=1002866&quot;&gt;published guide&lt;/a&gt; will only get you so far. You’ll find that
after rebooting, your changes will no longer be there.&lt;/p&gt;

&lt;p&gt;You can work around this limitation by rebuilding the keys on boot using
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/rc.local.d/local.sh&lt;/code&gt;. Somewhere before the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exit 0&lt;/code&gt;, add a modified
version of the following:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /etc/ssh/keys-&amp;lt;username&amp;gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ssh-rsa AAAAB3...&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; /etc/ssh/keys-&amp;lt;username&amp;gt;/authorized_keys
&lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;700 &lt;span class=&quot;nt&quot;&gt;-R&lt;/span&gt; /etc/ssh/keys-&amp;lt;username&amp;gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;600 /etc/ssh/keys-&amp;lt;username&amp;gt;/authorized_keys
&lt;span class=&quot;nb&quot;&gt;chown&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-R&lt;/span&gt; &amp;lt;username&amp;gt; /etc/ssh/keys-&amp;lt;username&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;username&amp;gt;&lt;/code&gt; and the value of the public key are replaced with actual
values.&lt;/p&gt;

&lt;p&gt;Once you’ve done this, ensure it’s backed up by running:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/sbin/auto-backup.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;auto-backup.sh&lt;/code&gt; &lt;a href=&quot;http://blogs.vmware.com/vsphere/2011/09/how-often-does-esxi-write-to-the-boot-disk.html&quot;&gt;runs every hour&lt;/a&gt; and backs up certain known
files which are marked with the “sticky bit” (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod +t file&lt;/code&gt;). These are then
restored on reboot. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;auto-backup.sh&lt;/code&gt; only knows about certain files, so we’re
adding this to one it knows about.&lt;/p&gt;

&lt;p&gt;This pattern can used in a similar way for other settings which need to stay
around after reboots.&lt;/p&gt;

&lt;p&gt;VMware seem to not recommend enabling SSH by default, and that’s fine.
If you disagree with that, you can enable it inside the Web UI by going to
“Manage”, “Services” finding “TM-SSH” and selecting “Actions”,
“Policy”, enable “Start and stop with host”.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Configuring VMware ESXi 6 on Hetzner</title>
        <link href="https://nickcharlton.net/posts/configuring-esxi-6-on-hetzner.html" />
        <id>https://nickcharlton.net/posts/configuring-esxi-6-on-hetzner.html</id>
        <published>Mon, 28 Nov 2016 00:00:00 +0000</published>
        <updated>Mon, 28 Nov 2016 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;https://hetzner.de&quot;&gt;Hetzner&lt;/a&gt; are a relatively low-cost hosting provider based out of Germany.
They provide a range of powerful but cheap dedicated servers which make a good
platform for experimenting with &lt;a href=&quot;http://www.vmware.com/products/vsphere-hypervisor.html&quot;&gt;VMWare ESXi&lt;/a&gt; or other virtualisation
software.&lt;/p&gt;

&lt;p&gt;However, their networking is based around statically routing MAC addresses
which makes it both a bit different to more common VLAN setups and harder to
get up and running. They do have a &lt;a href=&quot;https://wiki.hetzner.de/index.php/VMware_ESXi/en&quot;&gt;canonical guide to configuring this&lt;/a&gt;, but
I found my knowledge of networking a lacking from what it assumes and so I
found it a little hard to follow and caught myself in a few traps, so I thought
I’d write up some notes.&lt;/p&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;After you’ve ordered your dedicated server, go into &lt;a href=&quot;https://robot.your-server.de/server&quot;&gt;Robot&lt;/a&gt; and order your
desired subnet. When doing this, you’ll first want to request a standalone
“additional IP” and then have the subnet (of any size you’ll use) statically
routed to it. You may wish to mention it’s for ESXi here to make this clear.&lt;/p&gt;

&lt;p&gt;ESXi doesn’t support routing out of the box, so we’ll use the additional IP on
a router VM to provide a gateway for our subnet.&lt;/p&gt;

&lt;h2 id=&quot;installing-esxi-6&quot;&gt;Installing ESXi 6&lt;/h2&gt;

&lt;p&gt;If you’ve already done the IP configuration, or whilst you wait you can proceed
to install (or jump to the next section if you’ve done this).&lt;/p&gt;

&lt;p&gt;Using the &lt;a href=&quot;https://wiki.hetzner.de/index.php/LARA/en&quot;&gt;LARA&lt;/a&gt; console, mount the most recent available version of ESXi
that you can. I used
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VMware-VMvisor-Installer-6.0.0.update02-3620759.x86_64.iso&lt;/code&gt;, which is
available under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bootimages/vmware&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I’d recommend avoiding passwords with special characters as these didn’t seem
to pass through on my first attempt and I needed to re-install to get root
access.&lt;/p&gt;

&lt;h2 id=&quot;configuring-networking&quot;&gt;Configuring Networking&lt;/h2&gt;

&lt;h3 id=&quot;ip-addressing&quot;&gt;IP Addressing&lt;/h3&gt;

&lt;p&gt;We should now have two standalone IP addresses and a subnet. Let’s use these as
an example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Main IP: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;140.201.25.139&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Additional IP: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;140.201.300.61&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Subnet: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;140.201.2.32/28&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The subnet has usable addresses from: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;140.201.2.33&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;140.201.2.47&lt;/code&gt;, so
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.32&lt;/code&gt; defines the network and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.47&lt;/code&gt; is the broadcast address.&lt;/p&gt;

&lt;p&gt;I’ve used the first usable IP as the gateway for all of the VMs (the second IP
configured below in the router). Partly this is because this IP will be
pre-filled out by the Debian installer.&lt;/p&gt;

&lt;p&gt;We’ll then be able to create 12 VMs with public IP addresses, once we account
for those we’ll need to create to get it working.&lt;/p&gt;

&lt;h3 id=&quot;router-vm&quot;&gt;Router VM&lt;/h3&gt;

&lt;p&gt;When you first login to ESXi, you’ll find there are two networks. One is the
“Management Network” and the other is the “VM Network”. The “Management
Network” relates to the initial IP that was configured with your server. The
“VM Network” is the network available to VMs on this. We’ll not quite be able
to use this directly.&lt;/p&gt;

&lt;p&gt;To use our subnet, we’ll want to create a new vSwitch and Port Group. Under
“Networking”, create a new vSwitch (I called mine &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subnet0&lt;/code&gt;) and a Port Group
to go with it (I called mine &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Subnet 0&lt;/code&gt;). We’ll create a router VM which acts
as a gateway between this network and the “VM Network”.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/subnet0_network_topology.png&quot; alt=&quot;Subnet 0 Network Topology&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Subnet 0 Network Topology&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;To do this, create a new VM (I’m using Debian 8, configured with 512MB RAM and
a 32GB disk image) using the “VM Network” which came by default. You should
manually set the MAC address to the one provided inside &lt;a href=&quot;https://robot.your-server.de/server&quot;&gt;Robot&lt;/a&gt; for the
“additional IP”. DHCP will work and so the installer should autoconfigure
networking.&lt;/p&gt;

&lt;p&gt;Once this is up and running, shut it down and add a new network adapter. This
should be connected to the subnet you created above. All other settings can be
the default. Bring the VM back up and configure &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/networks/interfaces&lt;/code&gt; to
look something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;auto eth0
iface eth0 inet static
  address 140.201.300.61
  gateway 140.201.300.57
  netmask 255.255.255.248
  dns-nameservers 213.133.98.98 213.133.99.99

auto eth1
iface eth1 inet static
  address 140.201.2.33
  netmask 255.255.255.240
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eth1&lt;/code&gt; network needs neither a gateway (because we’re the gateway for the
network) or nameservers (we’ll be passing packets over without much interest).&lt;/p&gt;

&lt;p&gt;Finally for the router VM, configure IP forwarding so that packets will travel
through the router:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;echo 1 &amp;gt; /proc/sys/net/ipv4/ip_forward
echo 1 &amp;gt; /proc/sys/net/ipv6/conf/all/forwarding
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can persist this by adding:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;to ` /etc/sysctl.conf`.&lt;/p&gt;

&lt;p&gt;Now you can create a VM using one of your subnet IPs. Specify the IP address
and netmask, then the gateway IP as the one on the router connected to our
subnet vSwitch (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;140.201.2.33&lt;/code&gt; in this case). Your configuration will look
something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;auto eth0
iface eth0 inet static
  address 140.201.2.34
  gateway 140.201.2.33
  netmask 255.255.255.240
  dns-nameservers 213.133.98.98 213.133.99.99
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;further-subnets&quot;&gt;Further Subnets&lt;/h3&gt;

&lt;p&gt;This isn’t something I’ve yet tried, but if you wanted to configure a second
(or third, etc.) subnet you’d go about this in a similar manner.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Get the new subnet routed onto the “additional IP”.&lt;/li&gt;
  &lt;li&gt;Create another vSwitch and Port Group (perhaps, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subnet1&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;Add another network adapter to the router VM.&lt;/li&gt;
  &lt;li&gt;Connect this new network adapter to the new vSwitch.&lt;/li&gt;
  &lt;li&gt;Configure VMs in the same way.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You should now be able to bring up additional VMs and use the rest of your
assigned subnet!&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Resize all Safari windows with AppleScript</title>
        <link href="https://nickcharlton.net/posts/applescript-resize-all-safari-windows.html" />
        <id>https://nickcharlton.net/posts/applescript-resize-all-safari-windows.html</id>
        <published>Mon, 30 May 2016 00:00:00 +0000</published>
        <updated>Mon, 30 May 2016 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I have a particular preference for the size of my browser window, and I like
them all to be consistent. I divide my time between a 13” MacBook Pro and the
same device attached to a 27” Thunderbolt Display. Usually this works out
great, but over time, my browser windows will all end up being &lt;em&gt;slightly&lt;/em&gt;
different. Some will be a bit shorter, some wider, some off
centre.&lt;/p&gt;

&lt;p&gt;This is a pain to maintain manually, so in drops some &lt;a href=&quot;https://en.wikipedia.org/wiki/AppleScript&quot;&gt;AppleScript&lt;/a&gt;. I can
reset all visible windows to be the same size (across all Spaces) and be
centred on the screen. This is my goal:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/resized_safari_window.png&quot; alt=&quot;Resized Safari Window&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Resized Safari Window&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Here’s the AppleScript:&lt;/p&gt;

&lt;div class=&quot;language-applescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;575&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1212&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;tell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Finder&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;screen_resolution&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;desktop&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;tell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;screen_width&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;screen_resolution&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;screen_height&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;screen_resolution&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;tell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Safari&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;activate&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;reopen&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;screen_height&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;integer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;screen_hidth&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;integer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;every&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;tell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first two variables are set the size I’d like the window to be (it’s in
points). Over time, I’ll adjust this, so it’s best pulled out at the top.&lt;/p&gt;

&lt;p&gt;The script then goes on to calculate the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;y&lt;/code&gt; positions according to the
current screen resolution (this ensures it’s centred) and then applies this to
every window.&lt;/p&gt;

&lt;p&gt;This is a reasonably simple script, but it took a while to figure out. Such is
the nature of AppleScript.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: This doesn’t support multiple displays. The reported &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bounds&lt;/code&gt; returned
for the desktop include all of the displays combined together. We’d need to
figure out which windows belonged to which display and then calculate it that
way. This is a reasonable shortcoming for my usage.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A script is great, but not the most convenient. For this, I turned to
&lt;a href=&quot;https://alfredapp.com&quot;&gt;Alfred&lt;/a&gt; and put together a quick workflow to invoke it with: “Reset Safari
Windows”:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/resized_safari_window_workflow.png&quot; alt=&quot;Resized Safari Window Workflow&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Resized Safari Window Workflow&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;a href=&quot;/resources/reset_safari_window_positions.alfredworkflow&quot;&gt;You can
download the workflow here.&lt;/a&gt;&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Rails: Importing Local Data Dumps</title>
        <link href="https://nickcharlton.net/posts/rails-importing-local-data-dumps.html" />
        <id>https://nickcharlton.net/posts/rails-importing-local-data-dumps.html</id>
        <published>Mon, 11 Apr 2016 00:00:00 +0000</published>
        <updated>Mon, 11 Apr 2016 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I regularly work with production data (obfuscated when necessary) with local
&lt;a href=&quot;http://rubyonrails.org&quot;&gt;Rails&lt;/a&gt; apps. It’s much better to work with real-world data when building out
projects as it allows you to make decisions according to what will really
happen.&lt;/p&gt;

&lt;p&gt;But, importing them can be a bit of a pain. I’d previously been writing out the
command for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pg_import&lt;/code&gt; manually. So, I wrote a quick Rake task to do this for
me:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:db&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Import a given file into the database&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:import&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:environment&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dump_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;path&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;connection_config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection_config&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connection_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:adapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;postgresql&quot;&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;PGPASSWORD=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connection_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; pg_restore &quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;--verbose --clean --no-acl --no-owner &quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;--username=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connection_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;-d &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connection_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:database&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dump_path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;mysql&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;mysql2&quot;&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;mysql -u &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connection_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;-p&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connection_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connection_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:database&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &amp;lt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dump_path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;NotImplementedError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;An importer hasn&apos;t been implemented for: &quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connection_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:adapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This uses the database configuration for the current Rails environment to
invoke the standard tools for importing into &lt;a href=&quot;http://www.postgresql.org/docs/current/static/app-pgrestore.html&quot;&gt;Postgres&lt;/a&gt; and &lt;a href=&quot;https://dev.mysql.com/doc/refman/5.7/en/mysql-batch-commands.html&quot;&gt;MySQL&lt;/a&gt;. If a
different database adaptor is being used, it’ll safely fail with a message.&lt;/p&gt;

&lt;p&gt;Postgres doesn’t provide a command line manner in which to provide the
password, so we’re using the (&lt;a href=&quot;http://www.postgresql.org/docs/current/static/libpq-envars.html&quot;&gt;not-recommended&lt;/a&gt;) approach of using an
environment variable. In our case, this shouldn’t be an issue as this is
intending to be imported into a local development database.&lt;/p&gt;

&lt;p&gt;Rake’s argument handling can appear a little strange (because you can run
multiple commands at once, using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OptParser&lt;/code&gt; becomes a little bit more
complex), but it’s executed like so:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bundle &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;rake db:import[latest.dump]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;latest.dump&lt;/code&gt; is a file in the same directory as you’re executing the
command.&lt;/p&gt;

&lt;p&gt;This version supports just Postgres and MySQL as I don’t come across others so
regularly, more database adaptors are left as an exercise for the reader.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Docker via Homebrew</title>
        <link href="https://nickcharlton.net/posts/docker-via-homebrew.html" />
        <id>https://nickcharlton.net/posts/docker-via-homebrew.html</id>
        <published>Sun, 17 Jan 2016 00:00:00 +0000</published>
        <updated>Sun, 17 Jan 2016 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;On the site, &lt;a href=&quot;https://docker.com&quot;&gt;Docker&lt;/a&gt; recommend using the &lt;a href=&quot;https://docker.com/toolbox&quot;&gt;Docker Toolbox&lt;/a&gt; to get up and
running with it. I’m personally not much of a fan of these “platform”
installers; in attempting to provide a common solution for most, people like me
end up fighting with it. Fortunately, this can all be done standlone through
&lt;a href=&quot;http://brew.sh&quot;&gt;Homebrew&lt;/a&gt;. Here’s how to do it:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;docker docker-machine
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This pulls in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker&lt;/code&gt; executable and the new(er) way to managing machines
(as you can’t use Docker directly on OS X): &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-machine&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;creating-a-local-docker-machine&quot;&gt;Creating a Local Docker Machine&lt;/h2&gt;

&lt;p&gt;This will pull in a recent &lt;a href=&quot;https://github.com/boot2docker/boot2docker&quot;&gt;boot2docker&lt;/a&gt; image (platform dependent). You’ll
then need to set the environment variables so that Docker knows to use it.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker-machine create main
&lt;span class=&quot;nb&quot;&gt;eval&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;docker-machine &lt;span class=&quot;nb&quot;&gt;env &lt;/span&gt;main&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eval&lt;/code&gt; will set the following bits of configuration (although it’d vary for
you):&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DOCKER_TLS_VERIFY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;1&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DOCKER_HOST&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;tcp://192.168.237.131:2376&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DOCKER_CERT_PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/Users/nickcharlton/.docker/machine/machines/main&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DOCKER_MACHINE_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;main&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;with-vmware-fusion&quot;&gt;…with VMware Fusion&lt;/h3&gt;

&lt;p&gt;But, I prefer to use &lt;a href=&quot;https://www.vmware.com/uk/products/fusion/&quot;&gt;VMware Fusion&lt;/a&gt; for running VMs, so I do it this way
instead:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker-machine create &lt;span class=&quot;nt&quot;&gt;--driver&lt;/span&gt; vmwarefusion main
&lt;span class=&quot;nb&quot;&gt;eval&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;docker-machine &lt;span class=&quot;nb&quot;&gt;env &lt;/span&gt;main&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There we go. Docker that can be done nicely as with any other tool through
Homebrew.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Mirroring Bazaar Repositories with Git</title>
        <link href="https://nickcharlton.net/posts/mirroring-bazaar-repositories-with-git.html" />
        <id>https://nickcharlton.net/posts/mirroring-bazaar-repositories-with-git.html</id>
        <published>Tue, 21 Jul 2015 00:00:00 +0000</published>
        <updated>Tue, 21 Jul 2015 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://www.ubuntu.com&quot;&gt;Ubuntu&lt;/a&gt; uses &lt;a href=&quot;http://bazaar.canonical.com/en/&quot;&gt;bazaar&lt;/a&gt; as it’s source control system, with &lt;a href=&quot;https://launchpad.net&quot;&gt;Launchpad&lt;/a&gt; as
the hosting service. This is fine, apart from when you want to include
something which is maintained in &lt;a href=&quot;http://git-scm.com&quot;&gt;Git&lt;/a&gt; or otherwise doesn’t have good enough
support for it. In my case, I wanted to mirror a repository on GitHub so that
it could be included elsewhere as a submodule.&lt;/p&gt;

&lt;p&gt;Bazaar provides &lt;a href=&quot;http://doc.bazaar.canonical.com/bzr.2.6/en/user-reference/dpush-help.html&quot;&gt;dpush&lt;/a&gt; for this, but it wasn’t so obvious at first sight how
to work with it. (Replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;example&lt;/code&gt; with the appropriate name):&lt;/p&gt;

&lt;h3 id=&quot;1-ensure-youve-got-the-git-plugin&quot;&gt;1. Ensure You’ve Got the Git Plugin&lt;/h3&gt;

&lt;p&gt;The Git plugin provides the interoperability you’ll need. Some package
repositories will have as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bzr-git&lt;/code&gt; (&lt;a href=&quot;http://packages.ubuntu.com/trusty/bzr-git&quot;&gt;like Ubuntu does&lt;/a&gt;). You
may need to &lt;a href=&quot;http://doc.bazaar.canonical.com/plugins/en&quot;&gt;install it manually&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Something like this, perhaps:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ~/.bazaar/plugins
bzr branch lp:bzr-git git
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(You may need to create the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plugins&lt;/code&gt; directory.)&lt;/p&gt;

&lt;p&gt;You’ll probably also have to install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dulwich&lt;/code&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install dulwich&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;1-clone-the-bazaar-repository&quot;&gt;1. Clone the Bazaar Repository&lt;/h3&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bzr branch lp:example example
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;2-create-a-git-repository&quot;&gt;2. Create a Git Repository&lt;/h3&gt;

&lt;p&gt;You can create this where ever you usually create your hosted repositories.&lt;/p&gt;

&lt;p&gt;All you’ll need is the URL given to you to create the repo, in this case:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ssh://git@github.com/nickcharlton/example.git
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I was unable to use this directly, so instead gave &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bzr&lt;/code&gt; a hint by adjusting it
to:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git+ssh://git@github.com/nickcharlton/example.git
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;4-push-the-clone-to-the-remote&quot;&gt;4. Push the Clone to the Remote&lt;/h3&gt;

&lt;p&gt;The final step is to push the repository, including using the conventional
branch:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bzr dpush &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; git+ssh://git@github.com/nickcharlton/example.git,branch&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With Python installed via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew&lt;/code&gt;, I needed to ensure &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$PYTHONPATH&lt;/code&gt; was set
correctly, to the following:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PYTHONPATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/usr/local/lib/python2.7/site-packages&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You should now have a full copy of all of the original &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bazaar&lt;/code&gt; history, but
now all configured with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt;.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Terraform: AWS VPC with Private and Public Subnets</title>
        <link href="https://nickcharlton.net/posts/terraform-aws-vpc.html" />
        <id>https://nickcharlton.net/posts/terraform-aws-vpc.html</id>
        <published>Wed, 08 Jul 2015 00:00:00 +0000</published>
        <updated>Wed, 08 Jul 2015 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I’m currently in the process of designing out the architecture for a project
which is soon to be hosted on &lt;a href=&quot;http://aws.amazon.com/&quot;&gt;AWS&lt;/a&gt;. My aim has been to isolate groups of
components (like &lt;a href=&quot;http://redis.io&quot;&gt;Redis&lt;/a&gt; and/or &lt;a href=&quot;http://www.postgresql.org&quot;&gt;Postgres&lt;/a&gt; instances) from other groups
(like web application servers) as much as possible to restrict access.&lt;/p&gt;

&lt;p&gt;AWS provides &lt;a href=&quot;http://aws.amazon.com/documentation/vpc/&quot;&gt;VPC&lt;/a&gt; (Virtual Private Cloud) to do such a thing, but it’s quite
fiddly to get going. This is where &lt;a href=&quot;http://terraform.io&quot;&gt;Terraform&lt;/a&gt; steps in. Terraform is a tool
that allows you to automate your interactions with services like AWS (and
indeed others) and record the state, giving you the ability to place your
infrastructure in source control.&lt;/p&gt;

&lt;p&gt;In the AWS documentation, this is given as an example: “&lt;a href=&quot;http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Scenario2.html&quot;&gt;Scenario 2&lt;/a&gt;” and
this post will show how this can be replicated using Terraform. I’d recommend
you familiarise yourself with the AWS documentation’s version first and also
get a good understanding of &lt;a href=&quot;https://terraform.io/intro/&quot;&gt;Terraform from it’s
documentation&lt;/a&gt;. You can find a &lt;a href=&quot;https://github.com/nickcharlton/terraform-aws-vpc&quot;&gt;repository with the full
example over on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;terraform-briefly&quot;&gt;Terraform, Briefly&lt;/h3&gt;

&lt;p&gt;Terraform abstracts out the interaction with various infrastructure services
(AWS, Digital Ocean, OpenStack, etc) and provides a unified configuration
format for it.&lt;/p&gt;

&lt;p&gt;As the Terraform docs point out, the best way to show it is through examples,
but a few important points:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.tf&lt;/code&gt; files are all combined to provide the full configuration.
    &lt;ul&gt;
      &lt;li&gt;This gives us a handy way to break the configuration up into thematic
sections.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.tfstate&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.tfstate.backup&lt;/code&gt; holds the last-known state of the
infrastructure, you’ll want to store this, too.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.tfvars&lt;/code&gt; contain values for the declared variables, typically called:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform.tfvars&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To see what Terraform will do: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform plan -var-file terraform.tfvars&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To bring up the infrastructure we’ll run: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform apply -var-file
terraform.tfvars&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And to destroy it, we’ll run: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform destroy -var-file terraform.tfvars&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;infrastructure-aims&quot;&gt;Infrastructure Aims&lt;/h3&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/aws_terraform_network_diagram.png&quot; alt=&quot;AWS&apos;s
  Scenario 2 Network Diagram&quot; max-with=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;AWS&apos;s Scenario 2 Network Diagram&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The network diagram above is the best demonstration of what’ll be implemented:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The private subnet is inaccessible to the internet (both in and out).&lt;/li&gt;
  &lt;li&gt;The public subnet is accessible; just dependent on the configuration of the
security groups. Elastic IPs can be associated with instances in here.&lt;/li&gt;
  &lt;li&gt;Instances in the public subnet can access instances in the private subnet
(also dependent on security groups) because they’re in the same VPC (this is
enabled by the route tables).&lt;/li&gt;
  &lt;li&gt;Routing is handled like this:
    &lt;ul&gt;
      &lt;li&gt;Private subnet is routed through the NAT instance.&lt;/li&gt;
      &lt;li&gt;Public subnet is routed directly to the internet gateway.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;implementing-in-terraform&quot;&gt;Implementing in Terraform&lt;/h3&gt;

&lt;p&gt;In my implementation, I’ve opted to split the configuration into files broken
by what they do. I feel this is the one of the great strengths of the Terraform
configuration format, and, I find makes it much easier to track changes.&lt;/p&gt;

&lt;p&gt;Additionally, I’ve liberalised some of the security groups compared to the
original (allowing connections via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SSH&lt;/code&gt; from anywhere and allowing ICMP
packets). This is also specified to use Ubuntu 14.04 instances in the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eu-west-1&lt;/code&gt; (Ireland) region&lt;/p&gt;

&lt;h4 id=&quot;variablestf&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;variables.tf&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;This file is typically called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;variables.tf&lt;/code&gt; by convention. It’s typically full
of environment specific configuration, like which &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ami&lt;/code&gt; to use and which
credentials to use.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_access_key&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_secret_key&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_key_path&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_key_name&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_region&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;EC2 Region for the VPC&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;eu-west-1&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;amis&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;AMIs by region&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;eu&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;west&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ami-f1810f86&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# ubuntu 14.04 LTS&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;vpc_cidr&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;CIDR for the whole VPC&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;10.0.0.0/16&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;public_subnet_cidr&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;CIDR for the Public Subnet&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;10.0.0.0/24&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;private_subnet_cidr&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;CIDR for the Private Subnet&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;10.0.1.0/24&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Outside of the authentication credentials, here we’ve configured the default
AMI and the default region to use. Then the &lt;a href=&quot;https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing&quot;&gt;CIDR&lt;/a&gt; blocks for the VPC overall
and the two subnets contained within it.&lt;/p&gt;

&lt;h4 id=&quot;terraformtfvars&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform.tfvars&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;This is the file which is passed into each command, and provides the “secrets”
and more specific values.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;aws_access_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;aws_secret_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;aws_key_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;~/.ssh/aws.pem&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;aws_key_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(The key should be already configured with AWS.)&lt;/p&gt;

&lt;h4 id=&quot;awstf&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws.tf&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;This configures the provider, note how it uses string interpolation to pull
out the variables from previously:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;access_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${var.aws_access_key}&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;secret_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${var.aws_secret_key}&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;region&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${var.aws_region}&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;vpctf&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vpc.tf&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;This is the largest of the lot and configures both the VPC, the NAT instance,
the two subnets and the relevant security groups.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_vpc&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;default&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cidr_block&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${var.vpc_cidr}&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;enable_dns_hostnames&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;terraform-aws-vpc&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_internet_gateway&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;default&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vpc_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${aws_vpc.default.id}&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;sr&quot;&gt;/*
  NAT Instance
*/&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_security_group&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;nat&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;vpc_nat&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Allow traffic to pass from the private subnet to the internet&quot;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;ingress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;from_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;to_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tcp&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cidr_blocks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;${var.private_subnet_cidr}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ingress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;from_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;443&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;to_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;443&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tcp&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cidr_blocks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;${var.private_subnet_cidr}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ingress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;from_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;22&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;to_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;22&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tcp&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cidr_blocks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0.0.0.0/0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ingress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;from_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;to_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;icmp&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cidr_blocks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0.0.0.0/0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;egress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;from_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;to_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tcp&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cidr_blocks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0.0.0.0/0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;egress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;from_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;443&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;to_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;443&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tcp&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cidr_blocks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0.0.0.0/0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;egress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;from_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;22&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;to_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;22&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tcp&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cidr_blocks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;${var.vpc_cidr}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;egress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;from_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;to_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;icmp&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cidr_blocks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0.0.0.0/0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;vpc_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${aws_vpc.default.id}&quot;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;NATSG&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_instance&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;nat&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ami&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ami-30913f47&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# this is a special ami preconfigured to do NAT&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;availability_zone&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;eu-west-1a&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;instance_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;m1.small&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;key_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${var.aws_key_name}&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vpc_security_group_ids&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;${aws_security_group.nat.id}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;subnet_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${aws_subnet.eu-west-1a-public.id}&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;associate_public_ip_address&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;source_dest_check&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;VPC NAT&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_eip&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;nat&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${aws_instance.nat.id}&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vpc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;sr&quot;&gt;/*
  Public Subnet
*/&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_subnet&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;eu-west-1a-public&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vpc_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${aws_vpc.default.id}&quot;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;cidr_block&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${var.public_subnet_cidr}&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;availability_zone&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;eu-west-1a&quot;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Public Subnet&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_route_table&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;eu-west-1a-public&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vpc_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${aws_vpc.default.id}&quot;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;route&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cidr_block&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;0.0.0.0/0&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;gateway_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${aws_internet_gateway.default.id}&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Public Subnet&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_route_table_association&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;eu-west-1a-public&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;subnet_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${aws_subnet.eu-west-1a-public.id}&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;route_table_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${aws_route_table.eu-west-1a-public.id}&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;sr&quot;&gt;/*
  Private Subnet
*/&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_subnet&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;eu-west-1a-private&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vpc_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${aws_vpc.default.id}&quot;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;cidr_block&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${var.private_subnet_cidr}&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;availability_zone&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;eu-west-1a&quot;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Private Subnet&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_route_table&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;eu-west-1a-private&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vpc_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${aws_vpc.default.id}&quot;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;route&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cidr_block&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;0.0.0.0/0&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;instance_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${aws_instance.nat.id}&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Private Subnet&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_route_table_association&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;eu-west-1a-private&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;subnet_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${aws_subnet.eu-west-1a-private.id}&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;route_table_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${aws_route_table.eu-west-1a-private.id}&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The NAT instance is a special Amazon Linux AMI which handles the routing
correctly. It can be found on the AWS Marketplace using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws&lt;/code&gt; command line
tool by doing something like this:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws ec2 describe-images &lt;span class=&quot;nt&quot;&gt;--filter&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;owner-alias&quot;&lt;/span&gt;,Values&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;amazon&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--filter&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;name&quot;&lt;/span&gt;,Values&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;amzn-ami-vpc-nat*&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Like all AMIs, there’s specific images for each region.&lt;/p&gt;

&lt;h4 id=&quot;publictf&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;public.tf&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;This file (and the next) is where I’d split the file structure into more
specific sections (like a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web.tf&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;application.tf&lt;/code&gt;) instead of just the
single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;public.tf&lt;/code&gt; in a larger implementation, as this is where much of the
detail will be described. It works good enough here, as we’ll just be
implementing a single instance and security group in each subnet.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;sr&quot;&gt;/*
  Web Servers
*/&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_security_group&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;web&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;vpc_web&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Allow incoming HTTP connections.&quot;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;ingress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;from_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;to_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tcp&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cidr_blocks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0.0.0.0/0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ingress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;from_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;443&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;to_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;443&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tcp&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cidr_blocks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0.0.0.0/0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ingress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;from_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;to_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;icmp&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cidr_blocks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0.0.0.0/0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;egress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# SQL Server&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;from_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1433&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;to_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1433&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tcp&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cidr_blocks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;${var.private_subnet_cidr}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;egress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# MySQL&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;from_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3306&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;to_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3306&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tcp&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cidr_blocks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;${var.private_subnet_cidr}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;vpc_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${aws_vpc.default.id}&quot;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;WebServerSG&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_instance&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;web-1&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ami&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${lookup(var.amis, var.aws_region)}&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;availability_zone&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;eu-west-1a&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;instance_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;m1.small&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;key_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${var.aws_key_name}&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vpc_security_group_ids&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;${aws_security_group.web.id}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;subnet_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${aws_subnet.eu-west-1a-public.id}&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;associate_public_ip_address&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;source_dest_check&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;


    &lt;span class=&quot;n&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Web Server 1&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_eip&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;web-1&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${aws_instance.web-1.id}&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vpc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;privatetf&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;private.tf&lt;/code&gt;&lt;/h4&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;sr&quot;&gt;/*
  Database Servers
*/&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_security_group&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;db&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;vpc_db&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Allow incoming database connections.&quot;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;ingress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# SQL Server&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;from_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1433&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;to_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1433&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tcp&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;security_groups&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;${aws_security_group.web.id}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ingress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# MySQL&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;from_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3306&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;to_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3306&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tcp&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;security_groups&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;${aws_security_group.web.id}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;ingress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;from_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;22&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;to_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;22&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tcp&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cidr_blocks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;${var.vpc_cidr}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ingress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;from_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;to_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;icmp&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cidr_blocks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;${var.vpc_cidr}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;egress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;from_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;to_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tcp&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cidr_blocks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0.0.0.0/0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;egress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;from_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;443&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;to_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;443&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tcp&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cidr_blocks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0.0.0.0/0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;vpc_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${aws_vpc.default.id}&quot;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;DBServerSG&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;aws_instance&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;db-1&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ami&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${lookup(var.amis, var.aws_region)}&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;availability_zone&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;eu-west-1a&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;instance_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;m1.small&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;key_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${var.aws_key_name}&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;vpc_security_group_ids&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;${aws_security_group.db.id}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;subnet_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${aws_subnet.eu-west-1a-private.id}&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;source_dest_check&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;DB Server 1&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;bring-it-up--testing&quot;&gt;Bring it up &amp;amp; Testing&lt;/h3&gt;

&lt;p&gt;Once you’ve defined the environment configuration files, it’s time to bring it
up. You can do that by firing off:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;terraform apply &lt;span class=&quot;nt&quot;&gt;-var-file&lt;/span&gt; terraform.tfvars
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’ll bring up the VPC, all of the security groups, the NAT instance and
finally the Web and DB instances. You’ll then have a set of machines like so:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Instance Name&lt;/th&gt;
      &lt;th&gt;Private IP&lt;/th&gt;
      &lt;th&gt;Public IP&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;VPC NAT&lt;/td&gt;
      &lt;td&gt;10.0.0.210&lt;/td&gt;
      &lt;td&gt;52.16.161.59&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Web Server 1&lt;/td&gt;
      &lt;td&gt;10.0.0.37&lt;/td&gt;
      &lt;td&gt;52.16.185.18&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;DB Server 1&lt;/td&gt;
      &lt;td&gt;10.0.1.22&lt;/td&gt;
      &lt;td&gt;n/a&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;(Obviously, with different values).&lt;/p&gt;

&lt;p&gt;Our security groups allow us to connect through the NAT intuance to the other
instances inside the private subnet. I’d recommend configuring a separate
instance for this, which is typically known as a “bastion”. But, to keep it
simple, adjusting the security groups is enough for this case.&lt;/p&gt;

&lt;p&gt;Connect to the NAT instance, also using agent forwarding so we can reuse the
session:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ssh &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/key_name.pem &lt;span class=&quot;nt&quot;&gt;-A&lt;/span&gt; ec2-user@52.16.161.59
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then inside the NAT instance, you’ll be able to connect to either of the
other two instances:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Web Server 1&lt;/span&gt;
ssh ubuntu@10.0.0.37
&lt;span class=&quot;c&quot;&gt;# DB Server 1&lt;/span&gt;
ssh ubuntu@10.0.1.22
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The nuance of AWS security groups are out of scope for this article, but in
general, you’ll need to define a “egress” route for packets leaving an instance
and an “ingress” to define them going into (another) one. This gives you a
pretty powerful way to lock down access to specific instance groups.&lt;/p&gt;

&lt;p&gt;But, that’s it. You’ve now defined the state of an infrastructure in a group of
files, bought it all up in a single command and can track future changes with
it. All following Amazon’s example “Scenario 2”.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Installing Jenkins on OS X Yosemite</title>
        <link href="https://nickcharlton.net/posts/installing-jenkins-osx-yosemite.html" />
        <id>https://nickcharlton.net/posts/installing-jenkins-osx-yosemite.html</id>
        <published>Tue, 07 Jul 2015 00:00:00 +0000</published>
        <updated>Tue, 07 Jul 2015 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;https://jenkins-ci.org&quot;&gt;Jenkins&lt;/a&gt; is a &lt;a href=&quot;http://en.wikipedia.org/wiki/Continuous_integration&quot;&gt;continuous integration&lt;/a&gt; (CI) server written in Java. It’s
a pretty common solution for self-hosted CI servers.&lt;/p&gt;

&lt;p&gt;A lot of the documentation for installing on OS X is a little old (OS X has
changed a lot when it comes to say, Java, in the last few years) and it seemed
a good plan to write up something a bit newer.&lt;/p&gt;

&lt;p&gt;I host a Jenkins instance on a hosted Mac mini with &lt;a href=&quot;http://macminicolo.net&quot;&gt;Macminicolo&lt;/a&gt;. In
addition to Yosemite, it’s also got the &lt;a href=&quot;http://www.apple.com/uk/osx/server/&quot;&gt;OS X Server&lt;/a&gt; package installed,
Open Directory (which is Apple’s name for &lt;a href=&quot;http://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol&quot;&gt;LDAP&lt;/a&gt;) configured and a few other
tools. It’s typically used for &lt;a href=&quot;https://github.com/nickcharlton/boxes&quot;&gt;boxes&lt;/a&gt; builds.&lt;/p&gt;

&lt;p&gt;I’m assuming you’ll be installing Jenkins via &lt;a href=&quot;http://brew.sh&quot;&gt;Homebrew&lt;/a&gt; as the Jenkins
installer does some odd things around how the user is handled. I’m also
assuming you’re connected via VNC/Screen Sharing, as often it requires a GUI.&lt;/p&gt;

&lt;h3 id=&quot;1-install-java&quot;&gt;1. Install Java&lt;/h3&gt;

&lt;p&gt;You likely don’t have Java installed yet, so open a terminal and enter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;java&lt;/code&gt;
to request the install. Follow the instructions.&lt;/p&gt;

&lt;h3 id=&quot;2-create-a-user-for-jenkins&quot;&gt;2. Create a user for Jenkins&lt;/h3&gt;

&lt;p&gt;It’s best to run Jenkins as it’s own user (it can then be limited in the
permissions it has), and you’ll want to create a standard (full) user for it.&lt;/p&gt;

&lt;p&gt;You can do this through System Preferences, the Server Manager or the command
line.&lt;/p&gt;

&lt;p&gt;For a local user:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# create an applications group&lt;/span&gt;
dseditgroup &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; create &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; username &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; ‘Applications’ applications
&lt;span class=&quot;c&quot;&gt;# get the id for that group&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dscl &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-read&lt;/span&gt; /Groups/applications
&lt;span class=&quot;c&quot;&gt;# find a unique identifier to give the user&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dscl &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-list&lt;/span&gt; /Users UniqueID
&lt;span class=&quot;c&quot;&gt;# create the jenkins user&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dscl &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-create&lt;/span&gt; /Users/jenkins
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dscl &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-create&lt;/span&gt; /Users/jenkins PrimaryGroupID 505
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dscl &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-create&lt;/span&gt; /Users/jenkins UniqueID 1026
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dscl &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-create&lt;/span&gt; /Users/jenkins UserShell /bin/bash
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dscl &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-create&lt;/span&gt; /Users/jenkins RealName &lt;span class=&quot;s2&quot;&gt;&quot;Jenkins&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dscl &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-create&lt;/span&gt; /Users/jenkins NFSHomeDirectory /Users/jenkins
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dscl &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-passwd&lt;/span&gt; /Users/jenkins
&lt;span class=&quot;c&quot;&gt;# create and set the owner of the home directory&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo mkdir&lt;/span&gt; /Users/jenkins
&lt;span class=&quot;nb&quot;&gt;sudo chown&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-R&lt;/span&gt; jenkins /Users/jenkins
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(Replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;username&lt;/code&gt; with the username of an admin user).&lt;/p&gt;

&lt;p&gt;For an Open Directory user (replace the IP with the location of the relevant OD
tree):&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# create an applications group&lt;/span&gt;
dseditgroup &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; create &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; /LDAPv3/127.0.0.1 &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; diradmin &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; ‘Applications’ applications
&lt;span class=&quot;c&quot;&gt;# get the id for that group&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dscl &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; diradmin &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /LDAPv3/127.0.0.1 &lt;span class=&quot;nt&quot;&gt;-read&lt;/span&gt; /Groups/applications
&lt;span class=&quot;c&quot;&gt;# find a unique identifier to give the user&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dscl &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; diradmin &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /LDAPv3/127.0.0.1 &lt;span class=&quot;nt&quot;&gt;-list&lt;/span&gt; /Users UniqueID
&lt;span class=&quot;c&quot;&gt;# create the jenkins user&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dscl &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; diradmin &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /LDAPv3/127.0.0.1 &lt;span class=&quot;nt&quot;&gt;-create&lt;/span&gt; /Users/jenkins
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dscl &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; diradmin &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /LDAPv3/127.0.0.1 &lt;span class=&quot;nt&quot;&gt;-create&lt;/span&gt; /Users/jenkins PrimaryGroupID 505
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dscl &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; diradmin &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /LDAPv3/127.0.0.1 &lt;span class=&quot;nt&quot;&gt;-create&lt;/span&gt; /Users/jenkins UniqueID 1026
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dscl &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; diradmin &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /LDAPv3/127.0.0.1 &lt;span class=&quot;nt&quot;&gt;-create&lt;/span&gt; /Users/jenkins UserShell
/bin/bash
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dscl &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; diradmin &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /LDAPv3/127.0.0.1 &lt;span class=&quot;nt&quot;&gt;-create&lt;/span&gt; /Users/jenkins RealName &lt;span class=&quot;s2&quot;&gt;&quot;Jenkins&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dscl &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; diradmin &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /LDAPv3/127.0.0.1 &lt;span class=&quot;nt&quot;&gt;-create&lt;/span&gt; /Users/jenkins NFSHomeDirectory /Users/jenkins
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dscl &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; diradmin &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /LDAPv3/127.0.0.1 &lt;span class=&quot;nt&quot;&gt;-passwd&lt;/span&gt; /Users/jenkins
&lt;span class=&quot;c&quot;&gt;# create and set the owner of the home directory&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo mkdir&lt;/span&gt; /Users/jenkins &lt;span class=&quot;nb&quot;&gt;sudo chown&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-R&lt;/span&gt; jenkins /Users/jenkins
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You’ll now able to login as the Jenkins user by doing something like: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo -u
jenkins -i&lt;/code&gt; (this will login as Jenkins with a full user session).&lt;/p&gt;

&lt;h3 id=&quot;3-install-jenkins&quot;&gt;3. Install Jenkins&lt;/h3&gt;

&lt;p&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install jenkins&lt;/code&gt; as the user you’d normally use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew&lt;/code&gt; with.&lt;/p&gt;

&lt;h3 id=&quot;4-configure-the-launch-item&quot;&gt;4. Configure the Launch Item&lt;/h3&gt;

&lt;p&gt;OS X handles services using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launchd&lt;/code&gt; and has a few different types for where
they should be placed:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Type&lt;/th&gt;
      &lt;th&gt;Context&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;LaunchDaemon&lt;/td&gt;
      &lt;td&gt;System&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;LaunchAgent&lt;/td&gt;
      &lt;td&gt;User&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;More detail can be found in the &lt;a href=&quot;https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html&quot;&gt;Daemons and Services Programming Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In our case, we want to run Jenkins as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LaunchDaemon&lt;/code&gt; as our newly created
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jenkins&lt;/code&gt; user, so create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plist&lt;/code&gt; file as
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Library/LaunchDaemons/homebrew.mxcl.jenkins.plist&lt;/code&gt; with:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;plist&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Label&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;homebrew.mxcl.jenkins&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;ProgramArguments&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/usr/bin/java&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;-Dmail.smtp.starttls.enable=true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;-jar&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/usr/local/opt/jenkins/libexec/jenkins.war&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;--httpListenAddress=127.0.0.1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;--httpPort=8080&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;RunAtLoad&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;UserName&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;jenkins&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then load it: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo launchctl load
/Library/LaunchDaemons/homebrew.mxcl.jenkins.plist&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;(I’m sticking with the convention for naming as recommended by Hombrew.)&lt;/p&gt;

&lt;h3 id=&quot;5-test-it-out&quot;&gt;5. Test it out&lt;/h3&gt;

&lt;p&gt;Now you’ll be able to go to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://127.0.0.1:8080&lt;/code&gt; (locally) and see Jenkins.
You’ll want to verify that it’s launched with the correct user, which can be
found under “System Info”.&lt;/p&gt;

&lt;p&gt;The Launch Daemon specifies that it’ll only listen on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;127.0.0.1&lt;/code&gt;, so it’s not
possible to access it outside the local machine.&lt;/p&gt;

&lt;p&gt;(I’d recommend keeping Jenkins inside an internal network, as it’s executing
code on the local machine. Maybe by restricting it like below…)&lt;/p&gt;

&lt;h3 id=&quot;6-optional-reverse-proxying-apache--restricting-by-networks&quot;&gt;6. (Optional) Reverse Proxying Apache &amp;amp; Restricting by Networks&lt;/h3&gt;

&lt;p&gt;It’s likely that you’ll want to access Jenkins at a specific domain and not
have to use a separate port. This can be accomplished in lots of ways, but here
I’m going to explain &lt;a href=&quot;https://httpd.apache.org&quot;&gt;Apache&lt;/a&gt; as that’s what OS X Server uses by default. If
you’re not using OS X Server, either install Apache or another Web Server such
as &lt;a href=&quot;http://nginx.org&quot;&gt;nginx&lt;/a&gt;. The steps below configure this using SSL
(&lt;a href=&quot;https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/&quot;&gt;using this guide&lt;/a&gt;), you can cut out much of this if you’d prefer
not to.&lt;/p&gt;

&lt;p&gt;First, enable “Websites” in Server. It doesn’t need any special configuration
for what we’ll be doing.&lt;/p&gt;

&lt;p&gt;Next, create a new site from within Server. This will ensure it’s all working
in the manner it expects. Here, I’ve restricted it to internal only IPs:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/osx_server_jenkins_website.png&quot; alt=&quot;Jenkins Website Configuration&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Jenkins Website Configuration&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The configuration for “Websites” is held in:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Library/Server/Web/Config/apache2&lt;/code&gt;. There’s a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;README&lt;/code&gt; which might be helpful
to read.&lt;/p&gt;

&lt;p&gt;Next, reconfigure the configuration file for the new site (which is an Apache
virtualhost):&lt;/p&gt;

&lt;div class=&quot;language-conf highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# /Library/Server/Web/Config/apache2/sites/0000_10.0.0.1_443_jenkins.example.com.conf
&lt;/span&gt;&amp;lt;&lt;span class=&quot;n&quot;&gt;VirtualHost&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;:&lt;span class=&quot;m&quot;&gt;443&lt;/span&gt;&amp;gt;
    &lt;span class=&quot;n&quot;&gt;ServerName&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jenkins&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ServerAdmin&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;admin&lt;/span&gt;@&lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DocumentRoot&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/Library/Server/Web/Data/Sites/jenkins.example.com&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;DirectoryIndex&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;php&lt;/span&gt; /&lt;span class=&quot;n&quot;&gt;wiki&lt;/span&gt;/ /&lt;span class=&quot;n&quot;&gt;xcode&lt;/span&gt;/ &lt;span class=&quot;n&quot;&gt;default&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;html&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CustomLog&lt;/span&gt; /&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;apache2&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;access_log&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combinedvhost&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ErrorLog&lt;/span&gt; /&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;apache2&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;error_log&lt;/span&gt;
    &amp;lt;&lt;span class=&quot;n&quot;&gt;IfModule&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mod_ssl&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&amp;gt;
        &lt;span class=&quot;n&quot;&gt;SSLEngine&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;On&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SSLCipherSuite&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ALL:!aNULL:!ADH:!eNULL:!LOW:!EXP:RC4+RSA:+HIGH:+MEDIUM&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SSLProtocol&lt;/span&gt; -&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt; +&lt;span class=&quot;n&quot;&gt;TLSv1&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SSLProxyEngine&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;On&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SSLCertificateFile&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/etc/certificates/certificate_name.cert.pem&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SSLCertificateKeyFile&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/etc/certificates/certificate_name.key.pem&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SSLCertificateChainFile&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/etc/certificates/certificate_name.chain.pem&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SSLProxyProtocol&lt;/span&gt; -&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt; +&lt;span class=&quot;n&quot;&gt;TLSv1&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SSLProxyCheckPeerCN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;off&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SSLProxyCheckPeerName&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;off&lt;/span&gt;
    &amp;lt;/&lt;span class=&quot;n&quot;&gt;IfModule&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/Library/Server/Web/Data/Sites/jenkins.example.com&quot;&lt;/span&gt;&amp;gt;
        &lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;All&lt;/span&gt; -&lt;span class=&quot;n&quot;&gt;Indexes&lt;/span&gt; -&lt;span class=&quot;n&quot;&gt;ExecCGI&lt;/span&gt; -&lt;span class=&quot;n&quot;&gt;Includes&lt;/span&gt; +&lt;span class=&quot;n&quot;&gt;MultiViews&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;AllowOverride&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;
        &amp;lt;&lt;span class=&quot;n&quot;&gt;IfModule&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mod_dav&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&amp;gt;
            &lt;span class=&quot;n&quot;&gt;DAV&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Off&lt;/span&gt;
        &amp;lt;/&lt;span class=&quot;n&quot;&gt;IfModule&lt;/span&gt;&amp;gt;
        &amp;lt;&lt;span class=&quot;n&quot;&gt;IfDefine&lt;/span&gt; !&lt;span class=&quot;n&quot;&gt;WEBSERVICE_ON&lt;/span&gt;&amp;gt;
            &lt;span class=&quot;n&quot;&gt;Require&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;all&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;denied&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ErrorDocument&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;403&lt;/span&gt; /&lt;span class=&quot;n&quot;&gt;customerror&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;websitesoff403&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;html&lt;/span&gt;
        &amp;lt;/&lt;span class=&quot;n&quot;&gt;IfDefine&lt;/span&gt;&amp;gt;
    &amp;lt;/&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&amp;gt;

    &amp;lt;&lt;span class=&quot;n&quot;&gt;proxy&lt;/span&gt;&amp;gt;
        &lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Deny&lt;/span&gt;,&lt;span class=&quot;n&quot;&gt;Allow&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Deny&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;all&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Allow&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;127&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Allow&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;/&lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Allow&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;199&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;19&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;86&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;77&lt;/span&gt;
    &amp;lt;/&lt;span class=&quot;n&quot;&gt;proxy&lt;/span&gt;&amp;gt;

    &lt;span class=&quot;n&quot;&gt;ProxyPass&lt;/span&gt; / &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;://&lt;span class=&quot;m&quot;&gt;127&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;:&lt;span class=&quot;m&quot;&gt;8080&lt;/span&gt;/ &lt;span class=&quot;n&quot;&gt;nocanon&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ProxyPassReverse&lt;/span&gt; / &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;://&lt;span class=&quot;m&quot;&gt;127&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;:&lt;span class=&quot;m&quot;&gt;8080&lt;/span&gt;/
    &lt;span class=&quot;n&quot;&gt;ProxyRequests&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Off&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;AllowEncodedSlashes&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NoDecode&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;RequestHeader&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;Forwarded&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;Proto&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;RequestHeader&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;Forwarded&lt;/span&gt;-&lt;span class=&quot;n&quot;&gt;Port&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;443&quot;&lt;/span&gt;
&amp;lt;/&lt;span class=&quot;n&quot;&gt;VirtualHost&lt;/span&gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Much of this is the default configuration, with the bottom two additions being
the important bit:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;proxy&amp;gt;
    Order Deny,Allow
    Deny from all
    Allow from 127.0.0.1
    Allow from 10.0.0.1/8
&amp;lt;/proxy&amp;gt;

ProxyPass / http://127.0.0.1:8080/ nocanon
ProxyPassReverse / http://127.0.0.1:8080/
ProxyRequests Off
AllowEncodedSlashes NoDecode
RequestHeader set X-Forwarded-Proto &quot;https&quot;
RequestHeader set X-Forwarded-Port &quot;443&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…which &lt;a href=&quot;https://wiki.jenkins-ci.org/display/JENKINS/Running+Jenkins+behind+Apache&quot;&gt;configures it as a reverse proxy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, restart Apache:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo&lt;/span&gt; /Applications/Server.app/Contents/ServerRoot/usr/sbin/serveradmin stop web
&lt;span class=&quot;nb&quot;&gt;sudo&lt;/span&gt; /Applications/Server.app/Contents/ServerRoot/usr/sbin/serveradmin start web
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;7-optional-configure-users-with-ldap&quot;&gt;7. (Optional) Configure Users with LDAP&lt;/h3&gt;

&lt;p&gt;The final step I took was to configure Jenkins’ user support against OS X
Server’s Open Directory. This is just LDAP with a different name, and it’s easy
to get working.&lt;/p&gt;

&lt;p&gt;Under “Manage Jenkins” → “Configure Global Security”, configure the server
to the correct name and then fill in the root DN like so:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dc=server,dc=example,dc=com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;server&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;example&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;com&lt;/code&gt; make up the hostname of the configured LDAP
tree (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;server.example.com&lt;/code&gt;). This screenshot might help:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/osx_server_jenkins_ldap.png&quot; alt=&quot;Jenkins LDAP Configuration&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Jenkins LDAP Configuration&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Jenkins will be able to configure the rest itself.&lt;/p&gt;

&lt;h3 id=&quot;8-optional-manage-the-jenkins-process-with-brew-services&quot;&gt;8. (Optional) Manage the Jenkins Process with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew services&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;The default way to manage processes on OS X is using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launchctl&lt;/code&gt;, but the
syntax isn’t the easiest to use. For example, restarting Jenkins (something
you’ll want to do each time it’s upgrade):&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;launchctl unload /Library/LaunchDaemons/homebrew.mxcl.jenkins.plist
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;launchctl load /Library/LaunchDaemons/homebrew.mxcl.jenkins.plist
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The alternative is to use &lt;a href=&quot;https://github.com/Homebrew/homebrew-services&quot;&gt;brew services&lt;/a&gt;, which can make this much easier:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# first, install it:&lt;/span&gt;
brew tap homebrew/services

&lt;span class=&quot;c&quot;&gt;# then you can view the services:&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;brew services list

&lt;span class=&quot;c&quot;&gt;# restarting:&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;brew services restart jenkins
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;closing-steps&quot;&gt;Closing Steps&lt;/h3&gt;

&lt;p&gt;Before configuring your first set of builds, you’ll likely want to install a
set of plugins. I use:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://wiki.jenkins-ci.org/display/JENKINS/Git+Plugin&quot;&gt;Git&lt;/a&gt;, to support Git as a SCM type&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://wiki.jenkins-ci.org/display/JENKINS/Github+Plugin&quot;&gt;GitHub&lt;/a&gt;, for deeper integration&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://wiki.jenkins-ci.org/display/JENKINS/AnsiColor+Plugin&quot;&gt;AnsiColor&lt;/a&gt;, to support colours in terminal output&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://wiki.jenkins-ci.org/display/JENKINS/Ruby+Plugin&quot;&gt;Ruby&lt;/a&gt;, to support Ruby as a script type&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://wiki.jenkins-ci.org/display/JENKINS/Python+Plugin&quot;&gt;Python&lt;/a&gt;, to support Python as a script type&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Providing Internal DNS with OS X Server</title>
        <link href="https://nickcharlton.net/posts/osx-server-providing-internal-dns.html" />
        <id>https://nickcharlton.net/posts/osx-server-providing-internal-dns.html</id>
        <published>Mon, 06 Jul 2015 00:00:00 +0000</published>
        <updated>Mon, 06 Jul 2015 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Picture the scene: You’ve got a hosted Mac somewhere on the internet, you
connect to it via a VPN to access the services you host on it, but you’d like
to use domains to refer to these and have them look up correctly.&lt;/p&gt;

&lt;p&gt;The solution to this (and to a bunch of similar problems) is to configure a DNS
server internally to handle look ups for you. It’ll return internal-relevant
IPs for domains (which could in this case be anything).&lt;/p&gt;

&lt;p&gt;This is basically a much more specific version of &lt;a href=&quot;https://help.apple.com/advancedserveradmin/mac/4.0/#/apd1E0474ED-5AD9-4463-A37C-0307042475D7&quot;&gt;Apple’s “Provide DNS
service” article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;During these steps, you’ll need to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Configure a forwarding server to route requests outside of the local network.&lt;/li&gt;
  &lt;li&gt;Set the lookup behavior to serve the local network.&lt;/li&gt;
  &lt;li&gt;Configure the VPN to provide the DNS server in it’s configuration.&lt;/li&gt;
  &lt;li&gt;Add a relevant zone.&lt;/li&gt;
  &lt;li&gt;Add the names and aliases needed to that zone.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;configure-forwarding-servers&quot;&gt;Configure Forwarding Servers&lt;/h3&gt;

&lt;p&gt;It’s assumed you’ll configure this with whichever the upstream ISP provides.
You can do this in Server.app’s DNS section under forwarding servers. They
might well already be pre-populated.&lt;/p&gt;

&lt;h3 id=&quot;configure-lookup-behavior&quot;&gt;Configure Lookup Behavior&lt;/h3&gt;

&lt;p&gt;The lookup behavior defines which networks the DNS server will be available to.
You’ll want to configure both “The server itself” and “Clients on the local
network” as the screenshot below shows:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/osx_server_dns_lookups.png&quot; alt=&quot;DNS Server Lookup Behavior&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;DNS Server Lookup Behavior&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;configure-the-vpn-dns-settings&quot;&gt;Configure the VPN DNS Settings&lt;/h3&gt;

&lt;p&gt;Next, switch to the VPN service and configure the local DNS server:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/osx_server_vpn_dns_settings.png&quot; alt=&quot;VPN DNS Settings&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;VPN DNS Settings&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;(Assuming &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.0.0.1&lt;/code&gt; is the local server IP.)&lt;/p&gt;

&lt;h3 id=&quot;create-a-zone--configure-it&quot;&gt;Create a Zone &amp;amp; Configure it&lt;/h3&gt;

&lt;p&gt;Switch back to the DNS Service. First, check “Show All Records” in the cog
dropdown. Then, “Add Primary Zone”:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/osx_server_dns_primary_zone.png&quot; alt=&quot;Adding the Primary Zone&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Adding the Primary Zone&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Next, add an A record by selecting “Add Machine Record”:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/osx_server_dns_a_record.png&quot; alt=&quot;Adding an A record&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Adding an A record&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;testing&quot;&gt;Testing&lt;/h3&gt;

&lt;p&gt;Finally, you can test that this all worked by using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dig&lt;/code&gt;. On a device
connected to the VPN you should get a result like the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ dig test.example.com

; &amp;lt;&amp;lt;&amp;gt;&amp;gt; DiG 9.8.3-P1 &amp;lt;&amp;lt;&amp;gt;&amp;gt; test.example.com
;; global options: +cmd
;; Got answer:
;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, status: NOERROR, id: 42105
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 0

;; QUESTION SECTION:
;test.example.com.      IN  A

;; ANSWER SECTION:
test.example.com.   10800   IN  A   10.0.0.1

;; AUTHORITY SECTION:
example.com.        10800   IN  NS  test.example.com.

;; Query time: 153 msec
;; SERVER: 10.0.0.1#53(10.0.0.1)
;; WHEN: Mon Jul  6 10:34:46 2015
;; MSG SIZE  rcvd: 64
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you get no result (or it forwards up to the external &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;example.com&lt;/code&gt; zone, you
won’t see your internal IP listed. This would suggest that the DNS server isn’t
specified correctly, or one of the other steps didn’t quite work right.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Fixing Problems with OS X Yosemite Server</title>
        <link href="https://nickcharlton.net/posts/fixing-problems-with-yosemite-server.html" />
        <id>https://nickcharlton.net/posts/fixing-problems-with-yosemite-server.html</id>
        <published>Mon, 06 Jul 2015 00:00:00 +0000</published>
        <updated>Mon, 06 Jul 2015 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I have a hosted Mac mini with &lt;a href=&quot;http://macminicolo.net&quot;&gt;Macminicolo&lt;/a&gt; that I use for a range of
things, but predominantly as a build server for &lt;a href=&quot;https://github.com/nickcharlton/boxes&quot;&gt;boxes&lt;/a&gt; and other projects
(which require something like Xcode). However, there’s two problems which are
slightly outside of the GUI which crop up. Here’s a few notes on fixing those…&lt;/p&gt;

&lt;h3 id=&quot;configure-server-manager-to-use-a-custom-ssl-certificate&quot;&gt;Configure Server Manager to use a Custom SSL Certificate&lt;/h3&gt;

&lt;p&gt;If you’ve configured a custom SSL certificate, Server Manager will apply it to
all of the services managed by it (Websites, Mail, etc). I’ve tended to use a
wildcard one as it can then be reused across different places.&lt;/p&gt;

&lt;p&gt;Unfortunately, Server Manager doesn’t set the certificate for itself and so
you’ll end up with a warning when connecting from a remote machine. Like this:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/osx_server_certificate_failure.png&quot; alt=&quot;Server.app Certificate Failure&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Server.app Certificate Failure&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;This is easily fixed in the system keychain where the certificate is defined.&lt;/p&gt;

&lt;h4 id=&quot;1-open-keychain-access&quot;&gt;1. Open Keychain Access&lt;/h4&gt;

&lt;p&gt;Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Keychain Access&lt;/code&gt; (in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Applications/Utilities&lt;/code&gt;) and select the System
Keychain on the right-hand side.&lt;/p&gt;

&lt;h4 id=&quot;2-find-the-comappleservermgrd-identity-preference&quot;&gt;2. Find the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;com.apple.servermgrd&lt;/code&gt; Identity Preference&lt;/h4&gt;

&lt;p&gt;This is the setting that defines which certificate is used for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Server.app&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;From the “Preferred Certificate” dropdown, select the correct one. It’ll need
to match the domain on which you’re connecting to the server on (probably it’s
hostname, e.g.: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;server.example.com&lt;/code&gt;).&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/osx_server_identity_preference.png&quot; alt=&quot;Keychain Identity Preference&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Keychain Identity Preference&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;enable-open-directory-users-access-to-screen-sharing&quot;&gt;Enable Open Directory Users’ Access to Screen Sharing&lt;/h3&gt;

&lt;p&gt;By default, a new user created on an Open Directory tree isn’t able to use
Screen Sharing. This can be a bit of a problem if you’ve created a new admin
user via OD and then expect to be able to login again. There’s a few posts
around explaining this, but they’re all rather old (&lt;a href=&quot;https://discussions.apple.com/thread/1365257?start=0&amp;amp;tstart=0&quot;&gt;like this one on the Apple
Discussion boards&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;After an extensive amount of digging, I cornered everything down to two
separate steps.&lt;/p&gt;

&lt;h4 id=&quot;1-create-groups-for-apple-remote-desktop&quot;&gt;1. Create Groups for Apple Remote Desktop&lt;/h4&gt;

&lt;p&gt;The first part comes from Apple’s &lt;a href=&quot;https://ssl.apple.com/remotedesktop/pdf/ARD3_AdminGuide.pdf&quot;&gt;Remote Desktop documentation&lt;/a&gt;
(see Chapter 5, Page 63) which details the grid of permissions it relies upon.&lt;/p&gt;

&lt;p&gt;You’ll want to create all four groups (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ard_admin&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ard_reports&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ard_manage&lt;/code&gt;
and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ard_interact&lt;/code&gt;) and then add the relevant users to them. This can be done
on the command line like so (where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;username&lt;/code&gt; is the relevant user):&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dseditgroup -o create -n /LDAPv3/127.0.0.1 -u diradmin -p -r &apos;ard_admin&apos;
ard_admin&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dseditgroup -o edit -n /LDAPv3/127.0.0.1 -u diradmin -p -a username -t user
ard_admin&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Repeat the above whilst replacing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ard_admin&lt;/code&gt; with the other four groups.&lt;/p&gt;

&lt;h4 id=&quot;2-enable-directory-users&quot;&gt;2. Enable Directory Users&lt;/h4&gt;

&lt;p&gt;The next step is to configure Apple Remote Desktop to allow directory logins.
This can also be done by installing Apple Remote Desktop (locally) and building
a custom client installer  (there’s a step midway through to enable Directory
Logins). But the command line is easier:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo
/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart
-configure -clientopts -setdirlogins -dirlogins yes&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo
/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart
-restart -agent -console&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And now, you should be able to login over VNC using a directory user.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Static Sites with Rack and Sass</title>
        <link href="https://nickcharlton.net/posts/static-sites-with-rack-sass.html" />
        <id>https://nickcharlton.net/posts/static-sites-with-rack-sass.html</id>
        <published>Tue, 14 Apr 2015 00:00:00 +0000</published>
        <updated>Tue, 14 Apr 2015 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://rack.github.io&quot;&gt;Rack&lt;/a&gt; is the (excellent) common denominator web library for Ruby. &lt;a href=&quot;http://sass-lang.com&quot;&gt;Sass&lt;/a&gt;
also happens to be written in Ruby. Combining the two can be the perfect
solution to building &lt;a href=&quot;https://gdstechnology.blog.gov.uk/2014/12/11/govuk-living-style-guide/&quot;&gt;Living Styleguides&lt;/a&gt;, especially if you’re providing
them as an Gem.&lt;/p&gt;

&lt;p&gt;I did this on a recent project, but the documented combination of the two was a
bit lacking.&lt;/p&gt;

&lt;h3 id=&quot;static-sites-with-rack&quot;&gt;Static Sites with Rack&lt;/h3&gt;

&lt;p&gt;Rack is very barebones (it’s usually used behind the scenes in &lt;a href=&quot;http://www.sinatrarb.com&quot;&gt;Sinatra&lt;/a&gt; or
&lt;a href=&quot;http://rubyonrails.org&quot;&gt;Rails&lt;/a&gt;), but it does provide &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rack::Static&lt;/code&gt; which provides much of what
we’ll want to do.&lt;/p&gt;

&lt;p&gt;Following typical Ruby conventions, we’ll end up with a directory structure
which looks like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.
├── Gemfile
├── config.ru
└── public
    ├── index.html
    └── stylesheets
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Gemfile&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;https://rubygems.org&apos;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;rack&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;~&amp;gt; 1.6&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# config.ru&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;rack&apos;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rack&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Static&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;urls: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;/stylesheets&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;root: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;public&apos;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&apos;Content-Type&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;text/html&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&apos;Cache-Control&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;public, max-age=86400&apos;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;public/index.html&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RDONLY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then it can run with: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle exec rackup&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This will serve &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;public/index.html&lt;/code&gt; when called at: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:9292&lt;/code&gt; and
also serve the files in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;public/stylesheets&lt;/code&gt;. You can replicate that for other
directories (e.g.: for javascript).&lt;/p&gt;

&lt;h3 id=&quot;adding-sass-support&quot;&gt;Adding Sass Support&lt;/h3&gt;

&lt;p&gt;Sass comes with a Rack plugin that makes this easy. The difficulty is in
handling the paths for the source data. Adjust the above to look something like
this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Gemfile
source &apos;https://rubygems.org&apos;

gem &apos;rack&apos;, &apos;~&amp;gt; 1.6&apos;
gem &apos;sass&apos;, &apos;~&amp;gt; 3.4&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# config.ru
require &apos;rack&apos;
require &apos;sass/plugin/rack&apos;

use Sass::Plugin::Rack

use Rack::Static, urls: [&apos;/stylesheets&apos;], root: &apos;public&apos;

run lambda { |_env|
  [
    200,
    {
      &apos;Content-Type&apos; =&amp;gt; &apos;text/html&apos;,
      &apos;Cache-Control&apos; =&amp;gt; &apos;public, max-age=86400&apos;
    },
    File.open(&apos;public/index.html&apos;, File::RDONLY)
  ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The Sass plugin will assume it’s stylesheets are in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;public/stylesheets/sass&lt;/code&gt;
and compile them to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;public/stylesheets&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In my case, I was overriding this by doing (which is the current recommended
way):&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Sass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Plugin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_template_location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;app/assets/stylesheets&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Sass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Plugin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Rack&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This adjust the source directory for the Sass files, and in this case follows
along with the standard Rails convention for them. This is because it’s
structured to behave as a &lt;a href=&quot;http://guides.rubyonrails.org/engines.html&quot;&gt;Rails Engine&lt;/a&gt;.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Setting up Transparent Proxying VMs for mitmproxy</title>
        <link href="https://nickcharlton.net/posts/transparent-proxy-virtual-machines-mitmproxy.html" />
        <id>https://nickcharlton.net/posts/transparent-proxy-virtual-machines-mitmproxy.html</id>
        <published>Sat, 28 Mar 2015 00:00:00 +0000</published>
        <updated>Sat, 28 Mar 2015 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I seem to do a lot with Virtual Machines and this was no different. I’d started
out trying to reverse engineer an otherwise undocumented section of a client
for a hosting service I use and I was keen on configuring an isolated
environment for it.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://mitmproxy.org&quot;&gt;mitmproxy&lt;/a&gt; is a tool for intercepting HTTP and HTTPS traffic and then
allowing you to easily inspect it. In transparent proxy mode, it can sit at the
network level and intercept everything without any other configuration.&lt;/p&gt;

&lt;p&gt;There’s a few steps to it, and it seemed worth documenting:&lt;/p&gt;

&lt;h2 id=&quot;1-configure-two-vms&quot;&gt;1. Configure Two VMs&lt;/h2&gt;

&lt;p&gt;The first thing to do is to configure two VMs. I used a &lt;a href=&quot;http://www.ubuntu.com&quot;&gt;Ubuntu&lt;/a&gt; 14.04 LTS
install for the server and an &lt;a href=&quot;http://xubuntu.org&quot;&gt;Xubuntu&lt;/a&gt; (also 14.04 LTS) for the client. I
wanted a GUI on the client (for a web browser) and I had an Xubuntu ISO lying
around.&lt;/p&gt;

&lt;p&gt;You’ll want to install the virtual machine tools, too. (&lt;a href=&quot;https://github.com/nickcharlton/boxes&quot;&gt;boxes&lt;/a&gt; has a script
which might help). I named the server &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proxy-server&lt;/code&gt; and the client
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proxy-client&lt;/code&gt;. Otherwise they’re very standard configurations.&lt;/p&gt;

&lt;h2 id=&quot;2-setup-the-proxy-server&quot;&gt;2. Setup the Proxy Server&lt;/h2&gt;

&lt;p&gt;The proxy server will need two network interfaces, one to the outside world
(the default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eth0&lt;/code&gt;) and another for clients to connect on (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eth1&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;You’ll need to add a second network interface to the VM itself, with the new
one configured to be “internal only”. This sets up an isolated network on the
host machine which the VMs are able to communicate through.&lt;/p&gt;

&lt;p&gt;Once the virtual adaptor is added, we’ll configure that with a static IP:&lt;/p&gt;

&lt;p&gt;Edit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/network/interfaces&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Proxy Server network interface
auto eth1
iface eth1 inet static
address 192.168.3.1
netmask 255.255.255.0
gateway 0.0.0.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then bring it up: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo ifup eth1&lt;/code&gt;. You can verify it worked by checking the
response of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ifconfig -a&lt;/code&gt;. To understand what’s going on, you might find the
&lt;a href=&quot;https://help.ubuntu.com/lts/serverguide/network-configuration.html&quot;&gt;Ubuntu Documentation article on Network Configuration&lt;/a&gt;
helpful.&lt;/p&gt;

&lt;p&gt;The next step is to configure &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dnsmasq&lt;/code&gt; to provide us with DHCP and DNS on our
internal network. First install it: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo apt-get install dnsmasq&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/dnsmasq.conf&lt;/code&gt; with:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Listen for DNS requests on the internal network
interface=eth1
# Act as a DHCP server, assign IP addresses to clients
dhcp-range=192.168.3.10,192.168.3.100,96h
# Broadcast gateway and dns server information
dhcp-option=option:router,192.168.3.1
dhcp-option=option:dns-server,192.168.3.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The final step is to configure &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iptables&lt;/code&gt; to forward incoming traffic on ports
80 and 443 to our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mitmproxy&lt;/code&gt; instance:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 80 \
    -j REDIRECT --to-port 8080
iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 443 \
    -j REDIRECT --to-port 8080
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then we can install and open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mitmproxy&lt;/code&gt; in transparent mode:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ mitmproxy -T --host
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-T&lt;/code&gt; enables transparent proxy mode, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--host&lt;/code&gt; infers the hostname of the
request and displays that instead of the IP.)&lt;/p&gt;

&lt;h2 id=&quot;3-configure-the-client&quot;&gt;3. Configure the Client&lt;/h2&gt;

&lt;p&gt;The client will need pointing to the correct network, and then the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mitmproxy&lt;/code&gt;
certificates installed. Technically, installing the root CA for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mitmproxy&lt;/code&gt; is
optional, but without it you’ll get a lot of SSL warnings you need to jump
through.&lt;/p&gt;

&lt;p&gt;First, the network:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Reconfigure the client’s network adaptor to be “internal only”.&lt;/li&gt;
  &lt;li&gt;Set the network configuration to look like (through either the GUI or a
similar method to above):&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Address: 192.168.3.10
Subnet: 255.255.255.0
Gateway: 192.168.3.1
DNS: 192.168.3.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You’ll then want to test it all works. A non-HTTPS web page is likely the
easiest.&lt;/p&gt;

&lt;p&gt;Finally, add the generated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mitmproxy&lt;/code&gt; CA root certificate. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mitmproxy&lt;/code&gt;
generates these on first run, so you’ll want to take these from the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.mitmproxy&lt;/code&gt; directory on the host. You’re looking for
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mitmproxy-ca-cert.cer&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Take this from the proxy server and place it on the client. You’ll then want to
&lt;a href=&quot;http://askubuntu.com/a/377570&quot;&gt;make the certificate known to the OS&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;mitmproxy-ca-cert.cer /usr/local/share/ca-certificates/mitmproxy-ca-cert.crt
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;update-ca-certifcates
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(Note, as part of this, it’s renamed to have more usual &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crt&lt;/code&gt; extension, which
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update-certificates&lt;/code&gt; will pick up.)&lt;/p&gt;

&lt;p&gt;In the response you should see that a certificate was added. You can test this
all worked by doing something like: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wget https://nickcharlton.net&lt;/code&gt;. The
response should show up in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mitmproxy&lt;/code&gt; window.&lt;/p&gt;

&lt;p&gt;Firefox maintains it’s own certificate store and so you’ll want to add this in
in Preferences → Advanced → Certificates. Just add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mitmproxy-ca-cert.cer&lt;/code&gt;
file as an authority.&lt;/p&gt;

&lt;h2 id=&quot;4-begin-making-requests&quot;&gt;4. Begin Making Requests&lt;/h2&gt;

&lt;p&gt;You’ll now be able to make requests, both through a terminal or in a browser.&lt;/p&gt;

&lt;p&gt;For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wget https://nickcharlton.net&lt;/code&gt; should give you something that
looks like the next two screenshots:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/mitmproxy-host-list.png&quot; alt=&quot;mitmproxy Host
  List&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;mitmproxy Host List.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/mitmproxy-response.png&quot; alt=&quot;mitmproxy Response&quot; max-width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;mitmproxy Response.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Some of the details of this post &lt;a href=&quot;http://mitmproxy.org/doc/tutorials/transparent-dhcp.html&quot;&gt;comes from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mitmproxy&lt;/code&gt;
documentation&lt;/a&gt;, but threaded out with a bit more detail that I’d needed
to understand to get it all working. Now hopefully you’ll be able to replicate
a similar setup.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Conditionally Chaining ActiveRecord Queries</title>
        <link href="https://nickcharlton.net/posts/conditionally-chaining-activerecord-queries.html" />
        <id>https://nickcharlton.net/posts/conditionally-chaining-activerecord-queries.html</id>
        <published>Wed, 11 Mar 2015 00:00:00 +0000</published>
        <updated>Wed, 11 Mar 2015 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Sometimes, &lt;a href=&quot;http://guides.rubyonrails.org/active_record_querying.html&quot;&gt;ActiveRecord&lt;/a&gt; queries can get pretty complex, especially if
you’re implementing a feature like search over a typical “index” page that also
has pagination and the term itself is optional. Fortunately, ActiveRecord
queries can be chained in a few ways to make this a little bit nicer.&lt;/p&gt;

&lt;p&gt;The most common is like:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Nick Charlton&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;limit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which you’ll see often. But you can also do something like this, which works
really well for more complex queries:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Nick Charlton&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;email: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;nick@nickcharlton.net&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#=&amp;gt; &amp;lt;User id: 1, name: &apos;Nick Charlton&apos;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The result isn’t evaluated until you use the resulting object and so the
queries will be combined for you. This is because it returns an
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Relation&lt;/code&gt; object, and not the fully evaluated query.&lt;/p&gt;

&lt;p&gt;This can be a much cleaner solution to conditional filtering of records, like
you might wish to do with a reasonably complex search interface:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;articles&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Article&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;published: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;articles&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;articles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;thing&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:term&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’m following this pattern on a few projects where I’d previously branched on
the parameters given, but this is pretty unwieldy and leads to you duplicating
a bunch of code. This is much better.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Using Postmark with Sinatra</title>
        <link href="https://nickcharlton.net/posts/postmark-with-sinatra.html" />
        <id>https://nickcharlton.net/posts/postmark-with-sinatra.html</id>
        <published>Mon, 15 Dec 2014 00:00:00 +0000</published>
        <updated>Mon, 15 Dec 2014 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I usually use &lt;a href=&quot;https://postmarkapp.com&quot;&gt;Postmark&lt;/a&gt; for outgoing transactional email, I find this to be
better than expecting the underlying system to have a correctly configured
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sendmail&lt;/code&gt; and it helps with deliverability. &lt;a href=&quot;http://www.sinatrarb.com&quot;&gt;Sinatra&lt;/a&gt;, though, doesn’t have a
convention for handling email. The &lt;a href=&quot;http://www.sinatrarb.com/faq.html#email&quot;&gt;Sinatra FAQ&lt;/a&gt; lists an example using
&lt;a href=&quot;https://github.com/benprew/pony&quot;&gt;Pony&lt;/a&gt;, so going from there, here’s an example of using the &lt;a href=&quot;https://github.com/wildbit/postmark-gem&quot;&gt;Postmark Gem&lt;/a&gt;
with Sinatra:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;sinatra&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;postmark&apos;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:mailer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Postmark&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ApiClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/send_mail&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mailer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;deliver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;from: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;example@example.com&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;ss&quot;&gt;to: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;example@example.com&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;ss&quot;&gt;subject: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;A Test Email&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;ss&quot;&gt;text_body: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;A simple plain text test email.&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’m just using the standard settings handling to keep hold of the Postmark
client here. You could do it in any way. You can also render views (like you’d
typically do with ActionMailer), like so:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/send_html_mail&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mailer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;deliver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;from: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;example@example.com&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;ss&quot;&gt;to: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;example@example.com&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;ss&quot;&gt;subject: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;An HTML Test Email&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;ss&quot;&gt;html_body: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;erb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…where, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:email&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;email.erb&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;views/&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;A simple &lt;span class=&quot;nt&quot;&gt;&amp;lt;i&amp;gt;&lt;/span&gt;HTML&lt;span class=&quot;nt&quot;&gt;&amp;lt;/i&amp;gt;&lt;/span&gt; test email.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then you have simple Postmark email support. The rest of the &lt;a href=&quot;https://github.com/wildbit/postmark-gem&quot;&gt;Postmark Gem&lt;/a&gt;
documentation details everything else you’d be able to do with it.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Structuring Sinatra Applications</title>
        <link href="https://nickcharlton.net/posts/structuring-sinatra-applications.html" />
        <id>https://nickcharlton.net/posts/structuring-sinatra-applications.html</id>
        <published>Sat, 29 Nov 2014 00:00:00 +0000</published>
        <updated>Sat, 29 Nov 2014 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://www.sinatrarb.com&quot;&gt;Sinatra&lt;/a&gt; is a cool little framework for building web tools in Ruby. I’ve been
using it for years to build out little examples, tools like &lt;a href=&quot;https://github.com/nickcharlton/moviesapi&quot;&gt;moviesapi&lt;/a&gt;,
experimenting with REST and Hypermedia API design and now I’m using it to build
a relatively complex web service that provides the core of a new project I’m
part of.&lt;/p&gt;

&lt;p&gt;But I’ve never written down how (and why) I structure projects in the way I do.
Being such a tiny framework, Sinatra doesn’t impose any pattern on you (both a
blessing and a curse) and after several false starts (especially on the larger
projects), I’ve found I’ve settled into a pattern which works very well.&lt;/p&gt;

&lt;h2 id=&quot;small-ones&quot;&gt;Small Ones&lt;/h2&gt;

&lt;p&gt;The little applications are easy, it’s just one file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app.rb&lt;/code&gt;. This is
most often a ‘classic’ application, but sometimes it can be a ‘modular’ one
instead. For example:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# app.rb&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;sinatra&apos;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&apos;Hello World!&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The directory structure will usually end up looking something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.
├── Rakefile
├── app.rb
├── config.ru
└── spec
    ├── app_spec.rb
    └── spec_helper.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Directories like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;models&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lib&lt;/code&gt; fit quite well into the top level like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spec&lt;/code&gt;
does. &lt;a href=&quot;https://github.com/nickcharlton/moviesapi&quot;&gt;moviesapi&lt;/a&gt; provides a good working example of this.&lt;/p&gt;

&lt;h2 id=&quot;big-ones&quot;&gt;Big Ones&lt;/h2&gt;

&lt;p&gt;The structure for small applications doesn’t scale out very well because of the
focus on one single file. Once I’ve reached (or expect to reach) the stage where
I have several well grouped set of routes, I start to follow a &lt;a href=&quot;http://rubyonrails.org&quot;&gt;Rails&lt;/a&gt;-like
MVC pattern. This splits the routes into separate classes, almost exactly how
Rails handles things. Some of this originally came from &lt;a href=&quot;http://www.amazon.co.uk/gp/product/1449304230/ref=as_li_tl?ie=UTF8&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=1449304230&amp;amp;linkCode=as2&amp;amp;tag=nisbl-21&amp;amp;linkId=J677IJM5FWSRGT36&quot;&gt;Sinatra: Up and
Running&lt;/a&gt; which gives a nice overview.&lt;/p&gt;

&lt;p&gt;(Sometimes, using Rails is still a better approach than replicating it’s
structure like this. I keep with Sinatra for machine facing projects like APIs
once they get this big, anything else would be Rails.)&lt;/p&gt;

&lt;p&gt;This structure looks a lot like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.
├── Rakefile
├── app
│   ├── controllers
│   │   ├── application_controller.rb
│   │   └── example_controller.rb
│   ├── helpers
│   │   └── application_helper.rb
│   └── views
│       ├── example.erb
│       ├── layout.erb
│       └── not_found.erb
├── config.ru
└── spec
    ├── controllers
    │   ├── application_controller_spec.rb
    │   └── example_controller_spec.rb
    ├── helpers
    │   └── application_helper_spec.rb
    └── spec_helper.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app.rb&lt;/code&gt; has been broken out into a directory called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app&lt;/code&gt; instead. Inside
this are the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;controllers&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;helpers&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;views&lt;/code&gt; (and probably also &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;models&lt;/code&gt;, too)
which make up the application. In practice, each of the controllers inherit from
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sinatra::Base&lt;/code&gt; and so we keep the simple route call feature of the original
style and by inheriting from a common &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;application_controller&lt;/code&gt; we can provide a
few common configuration settings all around.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ApplicationController&lt;/code&gt; subclass could be as simple as just:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class ApplicationController; end&lt;/code&gt;, which should keep each of our controller
subclasses easy to reuse and test.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ApplicationController&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExampleController&lt;/code&gt; look like this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# application_controller.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApplicationController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Sinatra&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;helpers&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationHelper&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# set folder for templates to ../views, but make the path absolute&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:views&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expand_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;../../views&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;__FILE__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# don&apos;t enable logging when running tests&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:production&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:development&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;enable&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:logging&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# example_controller.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ExampleController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationController&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&apos;Example!&apos;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.ru&lt;/code&gt; is used to bring everything up together and this looks like this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# config.ru&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;sinatra/base&apos;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# pull in the helpers and controllers&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;./app/{helpers,controllers}/*.rb&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# map the controllers to routes&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;/example&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ExampleController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;/&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My biggest use of this structure has been on a recent project with a lot of
routes that have been exposed. It’s worked really well so far. That said, a lot
of my projects are of the much smaller type — the kind of projects where
Sinatra has always worked really well.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Reserve: Caching with Expiring Keys and Redis</title>
        <link href="https://nickcharlton.net/posts/reserve-caching-with-expire-keys-redis.html" />
        <id>https://nickcharlton.net/posts/reserve-caching-with-expire-keys-redis.html</id>
        <published>Sun, 12 Oct 2014 00:00:00 +0000</published>
        <updated>Sun, 12 Oct 2014 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I’ve just released a new RubyGem which makes caching objects (and allowing
them to expire) in &lt;a href=&quot;http://redis.io&quot;&gt;Redis&lt;/a&gt; easy. It’s called &lt;a href=&quot;https://rubygems.org/gems/reserve&quot;&gt;reserve&lt;/a&gt; and the &lt;a href=&quot;https://github.com/nickcharlton/reserve-ruby&quot;&gt;source is
on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This came from a desire to easily wrap common but slow tasks in a block that
would give me the advantages of caching but not increase the code complexity
significantly. We can do this by assuming that the output of most operations
could be serialised as JSON and then be thrown into a Redis instance. That means
that the simplest implementation could look like:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;reserve&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Reserve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reserve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;store&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:item&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;this is item&apos;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;item&lt;/code&gt; will be the hash object &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{ value: &apos;this is item&apos; }&lt;/code&gt; that will be
generated and stored on the first call, then subsequent calls will be cached
in redis until they expire. By default, the expiry time is set to 10800 seconds
(3 hours). After it expires, it’ll regenerate by executing the block again.&lt;/p&gt;

&lt;p&gt;The need behind this project was in caching responses from commonly used screen
scraping tools. I wanted to be able to wrap the code that was already being
used for the scraping so that on subsequent requests this was reused for a
set period of time. Redis works great for this because we can easily store
data with &lt;a href=&quot;http://redis.io/commands/set&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SET&lt;/code&gt;&lt;/a&gt;, and the expiry is also handled by Redis with
&lt;a href=&quot;http://redis.io/commands/expire&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EXPIRE&lt;/code&gt;&lt;/a&gt;. But there’s lots of other operations which work well with
this sort of time-based caching.&lt;/p&gt;

&lt;p&gt;With Reserve, the goal was to take those two Redis commands and have a very thin
layer between the application code and Redis. It should be possible to “wrap”
a variable assignment to add this sort of caching and not require all that
much more.&lt;/p&gt;

&lt;p&gt;I also wanted to be able to support a wide range of Redis drivers and keep the
rest of the dependancies as low as possible. I think it fits these requirements
quite well.&lt;/p&gt;

&lt;p&gt;Two places where this will be used are &lt;a href=&quot;http://urbanscraper.herokuapp.com&quot;&gt;UrbanScraper&lt;/a&gt; and &lt;a href=&quot;http://moviesapi.herokuapp.com&quot;&gt;moviesapi&lt;/a&gt;,
which I’ve been maintaining for a while.&lt;/p&gt;

&lt;p&gt;You can &lt;a href=&quot;https://github.com/nickcharlton/reserve-ruby&quot;&gt;browse the code on GitHub&lt;/a&gt;, view on &lt;a href=&quot;https://rubygems.org/gems/reserve&quot;&gt;RubyGems.org&lt;/a&gt;
and also &lt;a href=&quot;http://rubydoc.info/github/nickcharlton/reserve-ruby/master/frames&quot;&gt;read the docs at RubyDoc.info&lt;/a&gt;.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Site v4</title>
        <link href="https://nickcharlton.net/posts/site-v4.html" />
        <id>https://nickcharlton.net/posts/site-v4.html</id>
        <published>Wed, 20 Aug 2014 00:00:00 +0000</published>
        <updated>Wed, 20 Aug 2014 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;If you’re reading this, it means that I successfully rolled-out the fourth&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;
version of this site. It’s a small set of changes from before, but there is a
new style which accompanies a simpler design.&lt;/p&gt;

&lt;p&gt;On the design side, I wanted to deemphasize the latest set of posts (they’re
not, and never have been time dependent) and instead have it as a short
representation of me. Thus the blurb on the homepage. I also wanted to remove
the dedicated pages of static content (About, Projects, etc) which sets up for
having large amounts of text that doesn’t get updated (and, I suspect, people
don’t really want to read through).&lt;/p&gt;

&lt;p&gt;The project page does stay, but presented in a simpler manner and refreshed
with more recent ones. But the experiment with having a “links” section has
gone (those posts have been pushed into the posts).&lt;/p&gt;

&lt;p&gt;On the implementation, I’ve chopped out &lt;a href=&quot;http://sass-lang.com&quot;&gt;SASS&lt;/a&gt; because it’s mostly
unnecessary with so few styles and the amount of templates could also go down.
But, I did have to &lt;a href=&quot;/posts/custom-pandoc-options-hakyll-4.html&quot;&gt;implement a few changes to add support for HTML definition
lists&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And so, the site is updated for another year.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Sadly I’m not sure what the actual version should be, so I’m going with
  how long I’ve had a “modern” iteration of it. I’ve been reworking this
  every year for the past four and so this seemed to fit well. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</summary>
    </entry>
    
    <entry>
        <title>Custom Pandoc Options with Hakyll 4</title>
        <link href="https://nickcharlton.net/posts/custom-pandoc-options-hakyll-4.html" />
        <id>https://nickcharlton.net/posts/custom-pandoc-options-hakyll-4.html</id>
        <published>Wed, 20 Aug 2014 00:00:00 +0000</published>
        <updated>Wed, 20 Aug 2014 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://johnmacfarlane.net/pandoc/&quot;&gt;Pandoc&lt;/a&gt; has a huge set of extensions which are not enabled by default and
whilst &lt;a href=&quot;http://jaspervdj.be/hakyll/&quot;&gt;Hakyll&lt;/a&gt; does enable quite a few in its own options (like footnotes),
I wanted to add support for &lt;a href=&quot;http://johnmacfarlane.net/pandoc/README.html#definition-lists&quot;&gt;definition lists&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In Hakyll, the best way to do this seems to be to implement a custom content
compiler, as this can then be used everywhere you render content. This is
exactly what I did.&lt;/p&gt;

&lt;p&gt;The first thing was to look up the reference to the &lt;a href=&quot;http://jaspervdj.be/hakyll/reference/Hakyll-Web-Pandoc.html#g:2&quot;&gt;default compilers&lt;/a&gt; and
see what was already there. Hakyll provides:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pandocCompiler&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pandocCompilerWith&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pandocCompilerWithTransform&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second of these allows you to pass in options (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReaderOptions&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WriterOptions&lt;/code&gt;) and is actually called by the first but with only the default
options. The &lt;a href=&quot;http://jaspervdj.be/hakyll/reference/src/Hakyll-Web-Pandoc.html#pandocCompiler&quot;&gt;implementation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pandocCompiler&lt;/code&gt;&lt;/a&gt; looks like:&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;pandocCompiler&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Compiler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Item&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;pandocCompiler&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;pandocCompilerWith&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultHakyllReaderOptions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultHakyllWriterOptions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which gives us both the pattern to copy and the type declaration. The next
step is to add on custom options to the defaults. &lt;a href=&quot;http://hackage.haskell.org/package/pandoc-1.10.0.4/docs/Text-Pandoc-Options.html&quot;&gt;Pandoc gives a list of the
options that can be passed to it in it’s documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;defaultHakyllWriterOptions&lt;/code&gt; are &lt;a href=&quot;http://jaspervdj.be/hakyll/reference/src/Hakyll-Web-Pandoc.html#defaultHakyllWriterOptions&quot;&gt;defined as a set&lt;/a&gt;, so
we’ll need to add to that set and we’ll also need to be able to access the
&lt;a href=&quot;http://hackage.haskell.org/package/pandoc-1.10.0.4/docs/Text-Pandoc-Options.html&quot;&gt;Pandoc options&lt;/a&gt;, so first import those:&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;qualified&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Data.Set&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;S&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt;           &lt;span class=&quot;nn&quot;&gt;Text.Pandoc.Options&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, we need to build up the custom compiler. That looks like this:&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;customPandocCompiler&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Compiler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Item&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;customPandocCompiler&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;customExtensions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Ext_definition_lists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;defaultExtensions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;writerExtensions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultHakyllWriterOptions&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;newExtensions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foldr&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;insert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultExtensions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;customExtensions&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;writerOptions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultHakyllWriterOptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                          &lt;span class=&quot;n&quot;&gt;writerExtensions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newExtensions&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pandocCompilerWith&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultHakyllReaderOptions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;writerOptions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(To see it in place, you can see the &lt;a href=&quot;https://github.com/nickcharlton/nickcharlton.net/blob/c6b5b417c36c3b425ea1074d5d41d00425d202a6/site.hs#L125&quot;&gt;version in the repository&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;To walk through the compiler:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;We set the list of extensions we wish to apply.&lt;/li&gt;
  &lt;li&gt;Compute the default set of extensions.&lt;/li&gt;
  &lt;li&gt;Insert our list of extensions into the set.&lt;/li&gt;
  &lt;li&gt;Initialise the writer options.&lt;/li&gt;
  &lt;li&gt;Pass that over to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pandocCompilerWith&lt;/code&gt; compiler.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Subsequently, we can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;customCompiler&lt;/code&gt; everywhere we were previously using
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pandocCompiler&lt;/code&gt;, and without having to repeat our options.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Debugging SenTestingKit to XCTest Linker Errors in Upgraded Xcode Projects</title>
        <link href="https://nickcharlton.net/posts/debugging-sentestingkit-to-xctest-linker-errors.html" />
        <id>https://nickcharlton.net/posts/debugging-sentestingkit-to-xctest-linker-errors.html</id>
        <published>Mon, 18 Aug 2014 00:00:00 +0000</published>
        <updated>Mon, 18 Aug 2014 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;There’s a couple of (client) projects which I maintain which have been in
existence for quite a while (one still has full iOS 5 support and hopefully
we’ll be able to drop that soon), but they’re still well maintained and have
reasonable test suites that have followed them through rather well. Sadly,
Xcode can be a bit difficult and this time was no exception — although
it did take a long time before the issue was seen.&lt;/p&gt;

&lt;p&gt;In this project, I had a situation where the original &lt;a href=&quot;http://www.sente.ch/software/ocunit/&quot;&gt;SenTestingKit&lt;/a&gt; tests
had been upgraded to use &lt;a href=&quot;https://developer.apple.com/library/ios/documentation/ToolsLanguages/Conceptual/Xcode_Overview/UnitTestYourApp/UnitTestYourApp.html#//apple_ref/doc/uid/TP40010215-CH21-SW1&quot;&gt;XCTest&lt;/a&gt;, but now they were failing to build. It
looked to be a configuration error, so to come to the correct settings, I
compared a new project (so, created straight out of Xcode with nothing else)
and another project which was as new, but had &lt;a href=&quot;http://cocoapods.org&quot;&gt;Cocoapods&lt;/a&gt; attached. This
allowed me to get the correct (well, current) configuration and apply it back
to the old project.&lt;/p&gt;

&lt;p&gt;In the end, it appeared to have been caused by Xcode’s automatic upgrader
(which I’d run back when I upgraded the project) having mangled the build
settings. Here’s how I fixed it:&lt;/p&gt;

&lt;h2 id=&quot;update-the-framework-search-paths&quot;&gt;Update the Framework Search Paths&lt;/h2&gt;

&lt;p&gt;The first set of errors related to the imported headers being missing, and
looked like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ld: framework not found -XCTest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the test target under Xcode 5, the Header Search Paths listing should have
looked like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$(inherited)
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/upgraded_xcode_project_header_search_paths_listing.png&quot; alt=&quot;Updated Header Search Paths Listing&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;switch-the-linker-flag-from-sentestingkit-to-xctest&quot;&gt;Switch the linker flag from SenTestingKit to XCTest&lt;/h2&gt;

&lt;p&gt;The next set of error messages related to the testing framework being missing
and looked like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Undefined symbols for architecture i386 &quot;_OBJC_CLASS_$_XCTestCase&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This was caused by the old &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SenTestingKit&lt;/code&gt; still being linked aginst, and the
solution to this was to switch the “Other Linker Flags” setting to:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-Objc -framework XCTest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/upgraded_xcode_project_linker_flags.png&quot; alt=&quot;Updated Project Linker Flags&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Of course, by the time someone else comes across the same issue (or a similar)
one, the ideal configuration has likely changed somewhat. The trick to finding
the solution is to build new Xcode projects from the templates and compare
your configuration.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Ruby Subprocesses with stdout and stderr Streams</title>
        <link href="https://nickcharlton.net/posts/ruby-subprocesses-with-stdout-stderr-streams.html" />
        <id>https://nickcharlton.net/posts/ruby-subprocesses-with-stdout-stderr-streams.html</id>
        <published>Wed, 08 Jan 2014 00:00:00 +0000</published>
        <updated>Wed, 08 Jan 2014 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I’ve been doing a few things with Ruby which involve controlling and responding to
long-running processes, where the Ruby-based ‘wrapper’ takes the task of automating
something which is otherwise quite complex. Perhaps the best example is &lt;a href=&quot;https://github.com/nickcharlton/boxes&quot;&gt;boxes&lt;/a&gt;,
which uses a collection of &lt;a href=&quot;http://rake.rubyforge.org&quot;&gt;Rake&lt;/a&gt; tasks to generate &lt;a href=&quot;http://vagrantup.com&quot;&gt;Vagrant&lt;/a&gt; boxes using
&lt;a href=&quot;http://packer.io&quot;&gt;Packer&lt;/a&gt; –– each build takes somewhere in the region of twenty minutes to complete.&lt;/p&gt;

&lt;p&gt;But, I wanted to be able to more closely control the output (hiding much of it from
view) and react to events like build failures, which wasn’t possible by using
&lt;a href=&quot;http://ruby-doc.org/core-2.1.0/Kernel.html#method-i-system&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;system()&lt;/code&gt;&lt;/a&gt;. This needed to be able to handle the output as it came line 
by line without blocking (handling them as a stream), be able to handle &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stdout&lt;/code&gt; 
and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stderr&lt;/code&gt; independently, and allow me to collect all of the output from a 
subprocess (for providing as a sort-of stack trace).&lt;/p&gt;

&lt;p&gt;I went through many different solutions (using &lt;a href=&quot;http://www.ruby-doc.org/stdlib-2.1.0/libdoc/open3/rdoc/Open3.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Open3.popen3&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;http://ruby-doc.org/stdlib-2.1.0/libdoc/pty/rdoc/PTY.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PTY&lt;/code&gt;&lt;/a&gt; 
and others), before coming across this hybrid solution using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;popen3&lt;/code&gt; and separate 
threads for each output stream in &lt;a href=&quot;http://stackoverflow.com/a/1162850/83386&quot;&gt;this Stack Overflow post&lt;/a&gt; which met most of 
my requirements.&lt;/p&gt;

&lt;p&gt;This gave me a basic solution which looks like this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;open3&apos;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;./packer_mock.sh&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# see: http://stackoverflow.com/a/1162850/83386&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Open3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;popen3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# read each stream from a new thread&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stderr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;until&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw_line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nil?&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;parsed_line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:timestamp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw_line&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# append new lines&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parsed_line&lt;/span&gt;
        
        &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parsed_line&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# don&apos;t exit until the external process is done&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Line 3 pecifies the command that will be run. This is just a shell script which 
prints a multitude of characters for testing. Line 4 defines the final data 
structure; a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hash&lt;/code&gt; with two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array&lt;/code&gt;s for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stdout&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stderr&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The next interesting bits are Lines 9 and 10 which create a &lt;a href=&quot;http://ruby-doc.org/core-2.1.0/Thread.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Thread&lt;/code&gt;&lt;/a&gt; for 
handling the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stdout&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stderr&lt;/code&gt; streams seperately. Inside this the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;until&lt;/code&gt; 
block reads from the given stream, structures it and stores it. I’m adding a 
&lt;a href=&quot;http://www.ruby-doc.org/core-2.1.0/Time.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Time&lt;/code&gt;&lt;/a&gt; object here to aid my presentation of it later.&lt;/p&gt;

&lt;p&gt;Line 16 would be replaced by a conditional depending on the amount of verbosity the
user desired. Finally, Line 21 joins the thread once it has finished executing.&lt;/p&gt;

&lt;p&gt;This, then, allows me to continue handling long processes as a stream, but handle
each line individually. But the interface is a little awkward to use. Providing a
simpler command a single block could simplify this, something like:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Utils&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Subprocess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;./packer_mock.sh&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;stdout: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;simple output&quot;&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;stderr: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stderr&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &quot;error: an error happened&quot;&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;pid: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 12345&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which could be implemented like this rather impressively nested bit of code:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;open3&apos;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Utils&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Subprocess&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# see: http://stackoverflow.com/a/1162850/83386&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Open3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;popen3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# read each stream from a new thread&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stderr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;no&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;until&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nil?&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
              &lt;span class=&quot;c1&quot;&gt;# yield the block depending on the stream&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:out&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;block_given?&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;block_given?&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# don&apos;t exit until the external process is done&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Unlike the first approach, this just passes back the lines as strings which is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt;
if there’s no value. The final argument to the block is the &lt;a href=&quot;http://ruby-doc.org/core-2.1.0/Thread.html&quot;&gt;thread&lt;/a&gt; the subprocess 
is run as. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;thread.pid&lt;/code&gt; will give the &lt;a href=&quot;http://en.wikipedia.org/wiki/Process_identifier&quot;&gt;PID&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For now, this works pretty well for &lt;a href=&quot;https://github.com/nickcharlton/boxes&quot;&gt;boxes&lt;/a&gt; and will allow me to throw it into
something like &lt;a href=&quot;http://en.wikipedia.org/wiki/Jenkins_(software)&quot;&gt;Jenkins&lt;/a&gt; without a ridiculous amount of logs to parse to see which
ones build successfully.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Annual Review 2013</title>
        <link href="https://nickcharlton.net/posts/annual-review-2013.html" />
        <id>https://nickcharlton.net/posts/annual-review-2013.html</id>
        <published>Tue, 07 Jan 2014 00:00:00 +0000</published>
        <updated>Tue, 07 Jan 2014 00:00:00 +0000</updated>
        <summary type="html">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;For at least the past three years, I’ve been reading Chris Guillebeau’s 
&lt;a href=&quot;http://chrisguillebeau.com/3x5/category/annual-review/&quot;&gt;Annual Review&lt;/a&gt; series. He publishes a set of blog posts in December 
each year running through what he (and his business) did, what he thought of it and 
what he’d like to do in the upcoming year.&lt;/p&gt;

&lt;p&gt;I’ve been doing something similar myself for about the same time (I also used to
come up with a plan for summers back when I had long expanses of free-time; usually
a collection of things I wished to learn), but I never published them. Much of the
concept of the “Annual Review” is in doing it, but without publishing, it’s all too
easy to forget you did it and there’s a certain feeling of accountability when you’ve
published a block of self-criticism and plans for the year ahead. Especially when 
they all seem to pass by so quickly.&lt;/p&gt;

&lt;p&gt;This year was my final year of University, so that’s a general trend throughout.
The final push after several years of studying seems to throw out much of the other
things you want to do, and so the latter few months are much more interesting.&lt;/p&gt;

&lt;p&gt;But anyway, let’s begin…&lt;/p&gt;

&lt;h2 id=&quot;projects&quot;&gt;Projects&lt;/h2&gt;

&lt;p&gt;Nearly all of my projects (big and small) are something to do with programming, most
recently these seem to have refocused on Ruby. But for much of University, I spent
my time using Python (especially when &lt;a href=&quot;http://nickcharlton.net/tags/matplotlib.html&quot;&gt;graphs&lt;/a&gt; where involved), but after some 
niggling annoyances (no closing of blocks, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt;, time and date handling) I found
myself being pushed me back over to Ruby.&lt;/p&gt;

&lt;p&gt;Outside of Ruby, I’ve been spending it working with iOS, with a little bit of Mac
development interspersed. The introduction of iOS 7 has made for some interesting 
new challenges.&lt;/p&gt;

&lt;p&gt;I continue to maintain several projects which are hosted on &lt;a href=&quot;https://github.com/nickcharlton&quot;&gt;GitHub&lt;/a&gt; and of these 
I’ve been able to impart a stronger focus on automated tests. Several of them have 
been configured with &lt;a href=&quot;https://travis-ci.org&quot;&gt;Travis CI&lt;/a&gt;, but so far many of them have poor coverage,
especially the ones with a web component, or the needs of a complex environment. 
Next year, I expect to keep going down this path.&lt;/p&gt;

&lt;p&gt;There’s still a hardware project or two which I’ve not gotten around to thinking
much about since the end of University. The new year may (or may not) bring me to
coming back to them.&lt;/p&gt;

&lt;p&gt;I’m quite pleased with all of these smaller projects and I get quite a lot of
satisfaction out of building small well-tested libraries (before going away for
Christmas, I also released &lt;a href=&quot;https://github.com/nickcharlton/keypath-ruby&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keypath-ruby&lt;/code&gt;&lt;/a&gt; –– an approach for accessing 
nested Ruby collections).&lt;/p&gt;

&lt;p&gt;One of the things I’ve been less pleased with is the amount of time I’ve spent
working with servers and infrastructure. Whilst the work I’ve done with &lt;a href=&quot;http://www.packer.io/&quot;&gt;Packer&lt;/a&gt; 
has been going rather well (this bought about &lt;a href=&quot;http://boxes.nickcharlton.net/&quot;&gt;boxes&lt;/a&gt;), automating infrastructure 
using &lt;a href=&quot;http://www.getchef.com/chef/&quot;&gt;Chef&lt;/a&gt; has been a slow and quite painful process. I find it to be a 
frustrating tool to work with, but I’m also not hugely convinced by the alternatives.&lt;/p&gt;

&lt;p&gt;Sadly, this has kept me from working on some of the things I’ve really wanted to 
like &lt;a href=&quot;http://predicthesky.org/&quot;&gt;Predict the Sky&lt;/a&gt;, and a few others.&lt;/p&gt;

&lt;p&gt;But overall, this is probably the most pleasing section. I’m pleased with both the
odds and ends I’ve spent my time working on, and the bigger ones too.&lt;/p&gt;

&lt;h2 id=&quot;writing&quot;&gt;Writing&lt;/h2&gt;

&lt;p&gt;This year, I published 16 blog posts, most of them in August. I also added a 
link/commentry section to the site and published 23 links to (mostly long) articles 
I’ve come across. Over the year, the amount of visitors to this site have gone up
quite markedly. It’s nice to be read.&lt;/p&gt;

&lt;p&gt;After August, I seem to have come to a bit of a halt as I took on a much bigger
project. But I do wish I’d been writing along the way; instead I seem to have lots 
of drafts.&lt;/p&gt;

&lt;p&gt;So, as a plan, I aim to write &lt;em&gt;and publish&lt;/em&gt; more. I’m already quite good with
keeping a journal.&lt;/p&gt;

&lt;h2 id=&quot;reading&quot;&gt;Reading&lt;/h2&gt;

&lt;p&gt;Books. I love them. But I don’t read as many as I’d like. I’ve set myself a plan to
read 50 of them in 2014 and I’ll be interested to see how well I do.&lt;/p&gt;

&lt;p&gt;I’m currently reading &lt;a href=&quot;http://www.amazon.co.uk/gp/product/0141394811/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=0141394811&amp;amp;linkCode=as2&amp;amp;tag=nisbl-21&quot;&gt;Morrissey’s Autobiography&lt;/a&gt;, which turned out to 
be a surprisingly gripping dive into his mind, from his early days in school, to a
discovery of music and poetry to the Smiths, legal battles and his solo career. 
It’s a bit unfocused, but interesting. I’m most of the way through now.&lt;/p&gt;

&lt;p&gt;I’ve also ended up with subscriptions to the &lt;a href=&quot;http://www.newstatesman.com/&quot;&gt;New Statesman&lt;/a&gt;, &lt;a href=&quot;http://www.gq-magazine.co.uk/&quot;&gt;GQ&lt;/a&gt; and 
&lt;a href=&quot;http://www.newyorker.com&quot;&gt;The New Yorker&lt;/a&gt;, which regularly fill me with lots of different (and notably 
offline) things to read.&lt;/p&gt;

&lt;h2 id=&quot;travel&quot;&gt;Travel&lt;/h2&gt;

&lt;p&gt;With my final year of University, I didn’t travel much at all this year (apart from
between Plymouth and Surrey&lt;sup id=&quot;fnref:mum&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:mum&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; which doesn’t really count). I hope to fix this 
next year, with at least a trip or two to mainland Europe.&lt;/p&gt;

&lt;h2 id=&quot;studying&quot;&gt;Studying&lt;/h2&gt;

&lt;p&gt;Graduating is a curious thing. Suddenly you are propelled forth into the world,
expected to have some sort of plan. For most of us, this certainly isn’t the case.&lt;/p&gt;

&lt;p&gt;But after four years of University (one was on placement), I’m now the owner of a
Bachelors Degree in Computer Science.&lt;/p&gt;

&lt;h2 id=&quot;work&quot;&gt;Work&lt;/h2&gt;

&lt;p&gt;After taking a few months off to recover from University, I started freelancing in
June. First with some smaller projects, but then from a lucky bit of timing picking 
up something much larger.&lt;/p&gt;

&lt;p&gt;Next year I look forward to expanding the client base, and mixing up the projects 
a little. Some of this is already in motion.&lt;/p&gt;

&lt;h2 id=&quot;health--fitness&quot;&gt;Health &amp;amp; Fitness&lt;/h2&gt;

&lt;p&gt;University seemed to put this into quite a sharp decline. I used to cycle daily
when I was on placement, but I stopped on moving back to Plymouth. Now, I don’t do
anywhere near what I’d like to be doing.&lt;/p&gt;

&lt;p&gt;I intend to start running in 2014. But I’ve said this (mostly to people) before. 
I’ll see how I go in a year.&lt;/p&gt;

&lt;h2 id=&quot;into-2014&quot;&gt;Into 2014&lt;/h2&gt;

&lt;p&gt;2013 was interesting, and 2014 will mark my first complete year out of some sort of
education system. I’m really looking forward to it.&lt;/p&gt;

&lt;p&gt;I’m still seem to be on an academic calendar, so what most would consider some 
sort of “new years resolution” or some such has been in progress since September.
Be it balancing work with other projects, learning German through &lt;a href=&quot;http://duolingo.com/&quot;&gt;Duolingo&lt;/a&gt; or
one of a few other things.&lt;/p&gt;

&lt;p&gt;But in 2014, I want to finish learning to drive and acquire a car, get running 
regularly (I would like to be capable of running a 5k), keep going on my desire to 
dress much better, and chipping away at all of those other things that I do which
irritate me.&lt;/p&gt;

&lt;p&gt;It’s going to be a fun year.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:mum&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;My Mum’s. &lt;a href=&quot;#fnref:mum&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</summary>
    </entry>
    
    <entry>
        <title>Switching Season Report, 2013 Edition</title>
        <link href="https://nickcharlton.net/posts/switching-season-report-2013.html" />
        <id>https://nickcharlton.net/posts/switching-season-report-2013.html</id>
        <published>Wed, 28 Aug 2013 00:00:00 +0000</published>
        <updated>Wed, 28 Aug 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://al3x.net/2013/08/12/switching-season-annual-report-2013.html&quot;&gt;Alex Payne looks at the alternatives to his current setup and draws some conclusions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In his article, he’s put words to things I’ve so far been unable to describe to 
people myself:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“the Galaxy S4 is uninspired but good”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;“does not feel like a premium product. It is plasticky and sort of embarrassing 
to carry around, though its cheapness does lend it a sense of devil-may-care 
durability.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And, then:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Let’s not even speak of the system fonts, ugly but ignorable on a phone and 
downright offensive on a tablet. No, while much improved from several years ago, 
one does not use Android in 2013 for its looks.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On Linux as a deskop:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Trying to compute like a normal with Linux is, after all these years, still an
exercise in masochism.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yep.&lt;/p&gt;

&lt;p&gt;I sometimes get to play around with Android devices (it’s interesting to see how
they’re changing), and recently I acquired a ZTE One to have a look at &lt;a href=&quot;http://www.mozilla.org/en-US/firefox/os/&quot;&gt;Firefox OS&lt;/a&gt;.
I’m intending to write a review about that soon.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Alfred Workflow: Paste Cleanly</title>
        <link href="https://nickcharlton.net/posts/paste-cleanly.html" />
        <id>https://nickcharlton.net/posts/paste-cleanly.html</id>
        <published>Tue, 27 Aug 2013 00:00:00 +0000</published>
        <updated>Tue, 27 Aug 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;There is nothing more annoying than seeing, or ending up sharing URLs that look like
this (it’s split so it doesn’t look completely terrible…):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;http://www.nytimes.com/2013/08/25/opinion/sunday/ \
im-thinking-please-be-quiet.html? \
ref=opinion&amp;amp;_r=3&amp;amp;utm_source=buffer&amp;amp;utm_campaign=Buffer
&amp;amp;utm_content=buffer8fcfe&amp;amp;utm_medium=twitter&amp;amp;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’s not so much the efforts of marketing people to track how their URLs spread
around the web that annoys me, but more that it’s so damn ugly and far too long. 
But it is also happens to be the case that I don’t particularly care about 
marketing people’s feelings by me removing them.&lt;/p&gt;

&lt;p&gt;I’m already an avid user of &lt;a href=&quot;http://alfredapp.com/&quot;&gt;Alfred&lt;/a&gt;, where I use one of the example workflows to
allow me to use “Cmd + Shift + V” to paste as plain text. I figured the addition of
a little “cleaning” script based on a regex would be a nice way to implement it, so
I did&lt;sup id=&quot;fnref:check&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:check&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;Jump to the end if you’re just looking for the workflow.&lt;/p&gt;

&lt;h2 id=&quot;regex-and-test-pattern&quot;&gt;Regex and Test Pattern&lt;/h2&gt;

&lt;p&gt;The Google Analytics arguments all start with “utm_” (presumably standing for the
original Urchin product name). This is quite easy to match:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-regex&quot;&gt;(?i)(?:utm_+)[a-zA-Z]*=[a-zA-Z0-9]*(&amp;amp;)?
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This searches for “utm_”, a collection of other characters until an =, then another
collection of characters until either the end is reached, or it walks into an “&amp;amp;”.
It also does this case insensitively.&lt;/p&gt;

&lt;p&gt;This will correctly match/remove the offending string from all of the URLs below:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;http://example.com/slug?utm_content=test
http://example.com/slug?another=yep&amp;amp;utm_content=test
http://example.com/slug?utm_content=test&amp;amp;required=true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I was originally also matching either a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; at the start and removing that,
too. But, with the last example (which, actually, I haven’t seen in the wild), this
could potentially break the URL. Instead, I’m checking for a lost &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?&lt;/code&gt; on the end of
the URL before passing it back.&lt;/p&gt;

&lt;p&gt;And so, the workflow’s regex implementation:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{query}&apos;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# remove any matching Google tracking strings&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gsub!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/(?i)(?:utm_+)[a-zA-Z]*=[a-zA-Z0-9]*(&amp;amp;)?/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# if there&apos;s a trailing ?, remove it&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;end_with?&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;?&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;chop!&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# pass it back, without the newline&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;strip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;alfred-workflow&quot;&gt;Alfred Workflow&lt;/h2&gt;

&lt;p&gt;The workflow is based on the “Paste as plain text from hotkey” example, which I
already had mapped to “Cmd + Shift + V”. I’m just slotting through a “Run Script”
action before it is pasted.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/pastecleanly_workflow.png&quot; alt=&quot;The Paste Cleanly Workflow.&quot; /&gt;
  &lt;figcaption&gt;The Paste Cleanly Workflow.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;You can &lt;a href=&quot;/resources/pastecleanly.alfredworkflow&quot;&gt;download an archive of the workflow here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And so, there we go. As I get offended by more web-based atrocities[^strong], I’ll
likely add to the script more.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:check&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Something similar might already exist. It was quicker for me to build it
myself, than search the forums to see if someone else had done it already. But,
I suppose, this is rendered a bit moot now I’ve written a blog post on it.
[^strong]: Yeah, I know. That is a bit strong. &lt;a href=&quot;#fnref:check&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</summary>
    </entry>
    
    <entry>
        <title>Mocking Web Requests with VCR and MiniTest</title>
        <link href="https://nickcharlton.net/posts/mocking-web-requests-vcr-minitest.html" />
        <id>https://nickcharlton.net/posts/mocking-web-requests-vcr-minitest.html</id>
        <published>Thu, 15 Aug 2013 00:00:00 +0000</published>
        <updated>Thu, 15 Aug 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I just released &lt;a href=&quot;https://github.com/nickcharlton/moviesapi&quot;&gt;moviesapi&lt;/a&gt;. In &lt;a href=&quot;/posts/moviesapi.html&quot;&gt;the post I introduced it&lt;/a&gt;, I mentioned 
wanting to be able to add reliable tests. &lt;a href=&quot;https://twitter.com/benkeeping/status/365472072467628035&quot;&gt;Ben Keeping&lt;/a&gt; responsed suggesting that 
I have a look at &lt;a href=&quot;https://github.com/vcr/vcr/&quot;&gt;VCR&lt;/a&gt;. So I did.&lt;/p&gt;

&lt;p&gt;It’s a Ruby library that records the web requests that your application depends 
upon and saves it down to disk. On subsequent test runs, it reuses (“replays”) the 
previously saved data, vastly increasing the speed. For screenscraping tools like 
&lt;a href=&quot;https://github.com/nickcharlton/moviesapi&quot;&gt;moviesapi&lt;/a&gt; or &lt;a href=&quot;https://github.com/nickcharlton/UrbanScraper&quot;&gt;UrbanScraper&lt;/a&gt;, I can verify that my code is behaving correctly, 
even if the source has changed (this is another problem) and without constantly 
hitting the remote web service.&lt;/p&gt;

&lt;p&gt;Tutorials covering both VCR and MiniTest were a little thin on the ground, so I
thought I’d write one. As an introduction to MiniTest, I’d suggest &lt;a href=&quot;http://mattsears.com/articles/2011/12/10/minitest-quick-reference&quot;&gt;Matt Sears’
Quick Reference post&lt;/a&gt;. I’d also suggest giving the &lt;a href=&quot;https://github.com/vcr/vcr/&quot;&gt;VCR README&lt;/a&gt; 
at least a skim read.&lt;/p&gt;

&lt;p&gt;The overall application I’m testing is a &lt;a href=&quot;http://sinatrarb.com/&quot;&gt;Sinatra&lt;/a&gt; one, but here, I’m more
interested in testing the class that handles the screenscraping. Some of the
&lt;a href=&quot;http://recipes.sinatrarb.com/p/testing/minitest&quot;&gt;MiniTest suggestions came from the Sinatra Recipes site&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;gemfile&quot;&gt;Gemfile&lt;/h2&gt;

&lt;p&gt;Firstly, I added development and tests groups to my Gemfile, like so:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:development&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:test&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;minitest&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;~&amp;gt; 5.0.6&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;webmock&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;~&amp;gt; 1.12.0&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;vcr&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;~&amp;gt; 2.5.0&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;VCR is a high-level wrapper around a group of different web mocking libraries,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;webmock&lt;/code&gt; is just one of those supported. We’ll need this too.&lt;/p&gt;

&lt;p&gt;MiniTest is actually already in the standard library from Ruby 1.9, but I’m keeping
a reference for clarity (and a slightly newer version).&lt;/p&gt;

&lt;h2 id=&quot;spec-structure&quot;&gt;Spec Structure&lt;/h2&gt;

&lt;p&gt;I’m writing specs here, so everything is in a directory named “&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spec&lt;/code&gt;”:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;spec/
    cassettes/
    support/vcr_setup.rb
    spec_helper.rb
    movie_spec.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cassettes&lt;/code&gt; holds the recorded requests. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vcr_setup.rb&lt;/code&gt; contains VCR configuration
(it’s loaded by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spec_helper.rb&lt;/code&gt;). &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spec_helper.rb&lt;/code&gt; sets up the tests and provides
any common configuration. Finally, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;movie_spec.rb&lt;/code&gt; is the spec I’ll be running. 
It’s from &lt;a href=&quot;https://github.com/nickcharlton/moviesapi&quot;&gt;moviesapi&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;vcr_setuprb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vcr_setup.rb&lt;/code&gt;&lt;/h3&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;vcr&apos;&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;VCR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cassette_library_dir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;spec/cassettes&apos;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;hook_into&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:webmock&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This specifies where to find the cassettes — we assume everything is run from
the root of the project. Then it specifices which mocking library to use, in this
case, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;webmock&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;spec_helperrb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spec_helper.rb&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;This contains common code to all of the specs (or tests). It’s typically used to 
load in the application and run any common load configuration (like setting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENV&lt;/code&gt; 
to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt;) and is then included in each spec (or test) to make it available on test
run.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;minitest/autorun&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;minitest/pride&apos;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# pull in the VCR setup&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expand_path&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;./support/vcr_setup.rb&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__dir__&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# pull in the code to test&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expand_path&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;../movies.rb&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__dir__&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;rakefile&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rakefile&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;Finally, these additions to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rakefile&lt;/code&gt; will allow your tests to be running
according to typical Ruby conventions. It will also run the test suite as the
default rake task:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;rake/testtask&apos;&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Rake&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TestTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:spec&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;test_files&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;spec/*_spec.rb&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:default&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:spec&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For all of these helper files, they have been slimmed down a little. You may find
the &lt;a href=&quot;https://github.com/nickcharlton/moviesapi&quot;&gt;ones in the repo more helpful&lt;/a&gt;. (Also, these will work with Travis
CI.)&lt;/p&gt;

&lt;h2 id=&quot;writing-specs-that-use-vcr&quot;&gt;Writing Specs that use VCR&lt;/h2&gt;

&lt;p&gt;A typical MiniTest spec looks a bit like this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Something&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;before&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# something that should be done before a test starts&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;after&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# something that should be done after a test ends&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;does something&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# test things&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The MiniTest DSL provides several blocks that make up the spec. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;describe&lt;/code&gt;
block defines the behaviour you are specifying. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;it&lt;/code&gt; block defines the test
case. Then, inside here, “matchers” are used to confirm the output. MiniTest
&lt;a href=&quot;http://docs.seattlerb.org/minitest/Minitest/Expectations.html&quot;&gt;provides a reasonable collection of these in it’s docs&lt;/a&gt;, but you can
also define your own&lt;sup id=&quot;fnref:gist&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:gist&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;When using VCR with MiniTest, &lt;a href=&quot;https://github.com/vcr/vcr/wiki/Usage-with-MiniTest&quot;&gt;there are two approaches to work with&lt;/a&gt;.
The first is to manually specify a cassette to encapsulate the test run. This is
&lt;a href=&quot;https://www.relishapp.com/vcr/vcr/v/2-5-0/docs/getting-started&quot;&gt;described in the VCR Getting Started Guide&lt;/a&gt; and looks a bit like this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;VCR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;use_cassette&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;cassete name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# the test&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The second approach is to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;after&lt;/code&gt; blocks along with some 
runtime metadata that MiniTest provides. That looks a bit like this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Movies&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;before&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;VCR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;insert_cassette&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;after&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;VCR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eject_cassette&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;fetches a list of cinemas&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# the test&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;before&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;after&lt;/code&gt; are executed around each &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;it&lt;/code&gt; block. So here, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt; is 
“fetches a list of cinemas”. If you were to have multiple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;it&lt;/code&gt; blocks, cassettes 
would be defined for each. The cassettes are then saved in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spec/cassettes&lt;/code&gt;, in 
this case it is: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test_0001_fetches_a_list_of_cinemas.yml&lt;/code&gt;. This is quite a nice 
approach for having a cassette dynamically defined for each spec.&lt;/p&gt;

&lt;p&gt;For testing the result of the screenscraping, I have been checking the contents of 
the eventual data structure. Unlike with a typical web service, I can’t check the
data that is contained within. Similarly, the cassettes are commited to the
repository because I am checking for the behavioural correctness of my code —
not that the web service/site has changed and broken it. For testing the behaviour
of code that depends upon this, mocking will fit perfectly.&lt;/p&gt;

&lt;p&gt;MiniTest combines with VCR quite nicely — especially once you work out the
conventions to follow for structuring the test suite. If not for anything else,
mocking out the web requests like this saves a signficant amount of time when testing
web service interaction.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:gist&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;This gist by Jared Ning &lt;a href=&quot;https://gist.github.com/ordinaryzelig/2032303&quot;&gt;contains a good set of examples of defining your 
own&lt;/a&gt;. &lt;a href=&quot;#fnref:gist&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</summary>
    </entry>
    
    <entry>
        <title>Young Rewired State 2013</title>
        <link href="https://nickcharlton.net/posts/young-rewired-state-2013.html" />
        <id>https://nickcharlton.net/posts/young-rewired-state-2013.html</id>
        <published>Tue, 13 Aug 2013 00:00:00 +0000</published>
        <updated>Tue, 13 Aug 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;And so, sadly, that is the end of &lt;a href=&quot;https://youngrewiredstate.org/&quot;&gt;Young Rewired State&lt;/a&gt; for another year. Like
last year, I was a mentor down in &lt;a href=&quot;https://youngrewiredstate.org/centres/view/i-dat&quot;&gt;Plymouth&lt;/a&gt;, where we had five participants
and (overall) three mentors.&lt;/p&gt;

&lt;p&gt;Much like last year, this year was also incredible. After going through all of the
introductory material, then talking about sources of open data and technology 
options, we stepped back to give them some time to brainstorm. By the end of the
first day, they all had an idea of a project that they wanted to work on together.&lt;/p&gt;

&lt;p&gt;This turned into &lt;a href=&quot;http://things2do.ws/&quot;&gt;Things2Do&lt;/a&gt;, a simple search driven service which collates a
combination of different data sources (for places, weather forecasts, movies, etc)
and combines these to give a smart result based on your location (and what you
wish to do). It looks like this:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/things2do.png&quot; alt=&quot;Things2Do Homepage and Results&quot; width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Things2Do Homepage and Results&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;For now, the area and some of the data is limited to just Birmingham (this was just
for testing), but I’m sure they’ll fix that soon. It’s also responsive and well
tested across a range of different devices (although, there is a few bugs here and
there). I’m really impressed with how far they came in just a week. They have some 
interesting plans for it in the future, so I’ll be following along closely to see 
how it develops.&lt;/p&gt;

&lt;p&gt;For the whole week, we were almost completely hands off. We did help with some 
debugging, cross-browser issues, suggestions on approaches &amp;amp; direction, and I got 
to indulge myself in &lt;a href=&quot;/posts/moviesapi.html&quot;&gt;building a screenscraping tool and API&lt;/a&gt; to provide
some data that otherwise was unavailable to them, but the outcome was all of the
team’s effort.&lt;/p&gt;

&lt;p&gt;The Festival of Code was great, too. We arrived at about three in the afternoon
after a — long and standing room only — train journey. In the evening,
Sam was on stage along with &lt;a href=&quot;http://www.thereminhero.com&quot;&gt;TheraminHero&lt;/a&gt; and &lt;a href=&quot;http://www.pixelh8.co.uk&quot;&gt;Pixelh8&lt;/a&gt;. Then on Saturday 
morning, we had the first stage of judging, the “Heats” where the teams from 
different centres around the country presented their projects. Our group’s and all 
of the others went extremely well, with only a few technical hitches, it was great.&lt;/p&gt;

&lt;p&gt;Unlike last year, the final was kept to the Sunday. This meant everything was far
less rushed and as we had the heats in the morning, a free afternoon. Come Sunday, 
we filed into “Space 2”, ready to see who had made it into the final. Sadly, our 
team didn’t make it but they seemed more releaved in not having to do another demo! 
Seeing all of the other projects is always fascinating …the &lt;a href=&quot;http://hacks.youngrewiredstate.org/events/YRS2013/picycle&quot;&gt;PiBike&lt;/a&gt; seemed to be 
a favourite for many people with whom I spoke with and by the end, it had won the
award for Best in Show.&lt;/p&gt;

&lt;p&gt;I was hugely impressed by the judging, as I was last year. It’s all well and good
having a panel of interesting people handling the judging process, but if they
don’t ask the right kind of questions, debunk common fallacies&lt;sup id=&quot;fnref:design&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:design&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; and give
the presenters the space to get out what they mean to say, it is a huge
disservice to the people presenting. The judges handled it marvelously.&lt;/p&gt;

&lt;p&gt;After a little bit of time running over, the event wrapped up and we headed for our
train. It was an interesting (and hilariously overcrowded) journey back after a
great week.&lt;/p&gt;

&lt;p&gt;Over the weekend, &lt;a href=&quot;http://thisisthechris.co.uk/&quot;&gt;Chris&lt;/a&gt;, &lt;a href=&quot;http://wray.pro/&quot;&gt;Sam&lt;/a&gt; and I talked about what we’d like to do with
YRS in the South West in the future. We’d like to see more people, over a much
greater area of Devon and Cornwall. Currently, the closest centre to us is Bristol 
— which isn’t &lt;em&gt;really&lt;/em&gt; the South West when you are used to being in Plymouth
or Exeter. We did hear of people wishing to come but finding Plymouth (in addition 
to getting to Birmingham) all too expensive for the travel. We’ll be aiming to work 
out all of these for next year — we have several ideas for each of them.&lt;/p&gt;

&lt;p&gt;Anyway, if you’re interested, you can already &lt;a href=&quot;https://youngrewiredstate.org/events/gb/2014/festival-of-code-2014&quot;&gt;sign up for next year&lt;/a&gt; (as a 
mentor or as a partipant).&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:design&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Like Aral did with the rather common: “but we’re not designers!”. &lt;a href=&quot;#fnref:design&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</summary>
    </entry>
    
    <entry>
        <title>Tweetbot Mute Filters</title>
        <link href="https://nickcharlton.net/posts/tweetbot-mute-filters.html" />
        <id>https://nickcharlton.net/posts/tweetbot-mute-filters.html</id>
        <published>Tue, 13 Aug 2013 00:00:00 +0000</published>
        <updated>Tue, 13 Aug 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;The other day, &lt;a href=&quot;https://twitter.com/nickcharlton/status/366705942739423233&quot;&gt;I tweeted a link to a regex-based mute filter for Tweetbot&lt;/a&gt;.
The full URL looks like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;tweetbot:///mute/keyword?regex=1&amp;amp;text=(%3Fi)%23%3Fbreaking(%3F%3D%20%3Fbad)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It uses Tweetbot’s URL scheme&lt;sup id=&quot;fnref:scheme&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:scheme&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, to provide a predefined mute filter. This
way, a filter can be shared without copy and pasting it, which is quite nice. In
trying to work out how to do this, I ended up browsing around Twitter trying to
find an example of how to pass along a regex in the URL, as I had seen it done 
before. As I imagine I’ll do this again, I thought I’d write it up.&lt;/p&gt;

&lt;h2 id=&quot;regex&quot;&gt;Regex&lt;/h2&gt;

&lt;p&gt;Before it is encoded, the regex looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-regex&quot;&gt;(?i)#?breaking(?= ?bad)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It’s designed to match;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“breaking bad”&lt;/li&gt;
  &lt;li&gt;“breakingbad”&lt;/li&gt;
  &lt;li&gt;“#breakingbad”&lt;/li&gt;
  &lt;li&gt;“Breaking Bad”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and similar variations, but not;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“breaking”&lt;/li&gt;
  &lt;li&gt;“bad”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;when used alone.&lt;/p&gt;

&lt;p&gt;The first bit, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(?i)&lt;/code&gt; sets the regex to be case insensitive. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#?&lt;/code&gt; means it may or
may not start with a hash (matching hashtags or not). Then, the rest comprises of a
lookahead assertion.&lt;/p&gt;

&lt;p&gt;A lookahead assertion tests to see if a given set of characters are followed by
another set. It’s &lt;a href=&quot;http://www.regular-expressions.info/lookaround.html&quot;&gt;considered an assertion because it doesn’t consume these
characters&lt;/a&gt;, it will only match them. For testing regular expressions, I use
&lt;a href=&quot;http://krillapps.com/patterns/&quot;&gt;Patterns&lt;/a&gt;, so you get something that looks like this:&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/bb-regex.png&quot; alt=&quot;Testing in Patterns&quot; width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Testing in Patterns&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;You will see that whilst this matches the appropriate lines, it doesn’t match the
whole term. In this situation, this is fine (Tweetbot filters any tweet that would
match). So, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breaking(?= ?bad)&lt;/code&gt; looks for the word “breaking” followed by “bad”,
with or without a space between them. The lookahead assertion is the bit in
brackets, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(?=)&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;assembling-the-url&quot;&gt;Assembling the URL&lt;/h2&gt;

&lt;p&gt;The next bit is to make it valid inside a URL. I cheated and used Eric Meyer’s
&lt;a href=&quot;http://meyerweb.com/eric/tools/dencoder/&quot;&gt;URL Decoder/Encoder&lt;/a&gt;, but the invalid characters are below:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Character&lt;/th&gt;
      &lt;th&gt;Replacement&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;?&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%3F&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;#&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%23&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;=&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%3D&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Space&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%20&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;There are obviously many tools to help do that bit.&lt;/p&gt;

&lt;p&gt;So now, the next time I want to avoid spoilers to the finale of a pretty good TV
show, I’ll be save in the knowledge that once, I wrote down how it did it.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:scheme&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Many other iOS/Mac apps have a similar thing, it’s a bit of a hack to
have inter-application communication. &lt;a href=&quot;http://www.macstories.net/tag/url-scheme/&quot;&gt;Macstories has a bunch of posts on 
them&lt;/a&gt;. &lt;a href=&quot;#fnref:scheme&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</summary>
    </entry>
    
    <entry>
        <title>The Market for Used Mac minis</title>
        <link href="https://nickcharlton.net/posts/used-mac-minis.html" />
        <id>https://nickcharlton.net/posts/used-mac-minis.html</id>
        <published>Mon, 12 Aug 2013 00:00:00 +0000</published>
        <updated>Mon, 12 Aug 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://blog.macminicolo.net/post/57713423680&quot;&gt;Writing on the Macminicolo blog&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“you could have purchased an original G4 Mac mini for $500, used it as a server 
for 8 years, and sold it today for $100”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is nuts.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>iOS 7: Watershed Moment</title>
        <link href="https://nickcharlton.net/posts/ios-7-watershed-moment.html" />
        <id>https://nickcharlton.net/posts/ios-7-watershed-moment.html</id>
        <published>Mon, 12 Aug 2013 00:00:00 +0000</published>
        <updated>Mon, 12 Aug 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://gedblog.com/2013/07/18/watershed-moment/&quot;&gt;Gedeon Maheux talks about iOS 7 and new versions of apps&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Taking full advantage of new APIs, designing new interactions and more can 
represent a healthy investment, time is money after all. At what point in the 
update process does a developer decide she needs to charge for it? How many users 
will be alienated by charging again? Will these users be offset by the &lt;em&gt;huge&lt;/em&gt; 
influx of new people Apple brings to the table with the launch of the new OS?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For me, iOS 7 is absolutely a watershed moment. All of the advantages of an all-new 
iOS release (marketing, new APIs, much improved personal experience) has thrust me 
into working on new versions of Where’s Next? and thinking about how the Predict 
the Sky app will turn out.&lt;/p&gt;

&lt;p&gt;I’d be a fool not to want to run with the opportunity and release new versions 
— especially of Where’s Next? — and get a boost in the store and,
looking at Craig Hockenberry’s recent post on &lt;a href=&quot;http://furbo.org/2013/08/02/app-updates-for-ios-7/&quot;&gt;App Updates for iOS 7&lt;/a&gt; it 
doesn’t look like I’m alone.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>moviesapi: A Simple API for UK Cinema Listings</title>
        <link href="https://nickcharlton.net/posts/moviesapi.html" />
        <id>https://nickcharlton.net/posts/moviesapi.html</id>
        <published>Thu, 08 Aug 2013 00:00:00 +0000</published>
        <updated>Thu, 08 Aug 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;This week has been &lt;a href=&quot;http://youngrewiredstate.org/&quot;&gt;Young Rewired State&lt;/a&gt; week. At &lt;a href=&quot;https://github.com/yrsIDAT/2013&quot;&gt;YRS i-DAT&lt;/a&gt; in Plymouth, we’ve
had a team of five working on an tool to give you suggestions on what to do if you
were bored, all based upon your location.&lt;/p&gt;

&lt;p&gt;Among lots of other data sources, they wanted a way to get nearby cinemas and the
times of films which were showing there. After a quick hunt around for different
sources, it looked a bit bleak. Back in 2009, Yahoo had a movies API. Google had
one too as part of iGoogle, but that went when iGoogle was shut down.&lt;/p&gt;

&lt;p&gt;Then I came across &lt;a href=&quot;http://www.findanyfilm.com&quot;&gt;Find any Film&lt;/a&gt;. The first reference I could find for it was
a &lt;a href=&quot;http://www.theguardian.com/media/pda/2009/jan/28/digitalmedia-digitalvideo&quot;&gt;2009 Guardian article&lt;/a&gt;, explaining how the &lt;a href=&quot;http://industry.bfi.org.uk&quot;&gt;UK Film Council&lt;/a&gt; had
launched the site, and it mentioned that: “an API will also be rolled out that will 
allow developers to build applications around this unique and rich data set. ‘We’ll 
be thinking carefully about the best way of doing that.’”. Sadly, it looks like this
never happened.&lt;/p&gt;

&lt;p&gt;So, I took it upon myself to screenscrape Find any Film and spit out JSON formatted
data that could be reused by our YRS team. That became &lt;a href=&quot;http://moviesapi.herokuapp.com&quot;&gt;moviesapi&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s a small collection of Ruby, with the parsing handled by &lt;a href=&quot;http://nokogiri.org/&quot;&gt;Nokogiri&lt;/a&gt; and the
API implemented using &lt;a href=&quot;http://www.sinatrarb.com/&quot;&gt;Sinatra&lt;/a&gt;. It’s very similar to my last screenscraping
project, &lt;a href=&quot;http://urbanscraper.herokuapp.com/&quot;&gt;UrbanScraper&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As it is now, it implements the ability to find cinemas based on a postcode and to
find out the show times today at a given cinema. This was just enough to get it
working for the team, but I’ll probably add more functionality later on.&lt;/p&gt;

&lt;p&gt;At some point, I need to add some tests&lt;sup id=&quot;fnref:tests&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:tests&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, and it would be nice to run it
through &lt;a href=&quot;http://travis-ci.org&quot;&gt;Travis&lt;/a&gt; too. Additionally, some caching (probably using Redis) would be
good — the requests are quite slow and don’t change all that often.&lt;/p&gt;

&lt;p&gt;The legal status of this data is a little complex. As far as I can understand, any
user (including me, and then you using the API) would be bound by the Find any Film
&lt;a href=&quot;http://www.findanyfilm.com/terms-and-conditions&quot;&gt;Terms and Conditions&lt;/a&gt; which says that you cannot use the data commercially along
with a bunch of other things. It’d be nice if people would just share this kind of
data, but that’s another complex argument. I’m of the opinion that I’ll continue to
run such a thing (doing as much as I can to stop Find any Film being abused by
caching at my end) until I’m told otherwise.&lt;/p&gt;

&lt;p&gt;Anyway, &lt;a href=&quot;https://github.com/nickcharlton/moviesapi&quot;&gt;the source is on GitHub&lt;/a&gt; and the 
&lt;a href=&quot;http://moviesapi.herokuapp.com&quot;&gt;live API is hosted on Heroku&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:tests&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;I’m not sure how to test screenscraping tools (UrbanScraper is in the same
boat and also needs them). My thoughts fell along testing the existence of 
returned data and their types. &lt;a href=&quot;#fnref:tests&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</summary>
    </entry>
    
    <entry>
        <title>Automated Backups with backup and Rsync.net</title>
        <link href="https://nickcharlton.net/posts/automated-backups-with-backup-and-rsyncnet.html" />
        <id>https://nickcharlton.net/posts/automated-backups-with-backup-and-rsyncnet.html</id>
        <published>Fri, 02 Aug 2013 00:00:00 +0000</published>
        <updated>Fri, 02 Aug 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;For the past 18 months, I’ve been meaning to review the way I go about doing backups.
Whilst I’m quite settled on backing up workstations, until about two weeks ago I
had a half-configured, non-recoverable solution which was only setup on one out of
several servers. That’s not very useful.&lt;/p&gt;

&lt;p&gt;As well as the obvious requirement for something automated, I also wanted something 
which could allow me to reuse much of the backup definitions across multiple servers
but also allowed me to avoid writing bash scripts. I’d come across the &lt;a href=&quot;https://github.com/meskyanichi/backup&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backup&lt;/code&gt;
Ruby gem&lt;/a&gt; some time ago and after some quick testing, this seemed to work
quite well.&lt;/p&gt;

&lt;p&gt;Then, I needed some sort of remote host to backup to. I had heard of &lt;a href=&quot;http://www.rsync.net/&quot;&gt;Rsync.net&lt;/a&gt; 
through the &lt;a href=&quot;http://prgmr.com/&quot;&gt;prgmr&lt;/a&gt; mailing list. They provide a reasonably cheap remote filesystem
with which you can access over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh&lt;/code&gt; (and so, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scp&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rsync&lt;/code&gt; work). On the machine
being backed up, this is great as I don’t need any special tools to upload backups
and for restoring the same applies.&lt;/p&gt;

&lt;h2 id=&quot;backup-strategy&quot;&gt;Backup Strategy&lt;/h2&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backup&lt;/code&gt; gem uses the concepts of “models” to define a “backup”. This consists
of any directories or databases you may wish to backup, along with any processing
you may wish to do with it (compressing, encrypting, etc) and where to store it.&lt;/p&gt;

&lt;p&gt;My approach to the models is to create different ones for the different “roles” a system
performs. By default, this includes a “base” role which archives &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/home&lt;/code&gt;
and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/log&lt;/code&gt; (in a multi-user system, I’d probably split out &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/home&lt;/code&gt; to it’s own
model). Then, additional models are defined for more specific roles. So, to backup
a Wordpress site, a “site” model would first backup the associated database and
then collect up the data in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/www/site/&lt;/code&gt; into the one archive.&lt;/p&gt;

&lt;p&gt;Each model is set to be compressed using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gzip&lt;/code&gt;, upload to &lt;a href=&quot;http://www.rsync.net/&quot;&gt;Rsync.net&lt;/a&gt; and then 
notify me if any errors occur. It also keeps the last 5 versions of the backup.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backup&lt;/code&gt; handles organising the subsequent archives on the remote quite well. For
each server, I define a directory to collect them in and then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backup&lt;/code&gt; sorts each
run by a directory named after the timestamp. Each model is then collected inside.
Much like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;server_name/
    base/
        2013.07.29.21.54.01/
            base.tar
        2013.07.30.00.30.07/
            base.tar
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, a cron job is used to automate it to run daily.&lt;/p&gt;

&lt;h2 id=&quot;rsyncnet&quot;&gt;rsync.net&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://www.rsync.net/&quot;&gt;Rsync.net&lt;/a&gt; is refreshingly simple (a bit like &lt;a href=&quot;http://prgmr.com/&quot;&gt;prgmr&lt;/a&gt; is, I suppose). After
signing up, you’ll get sent the details needed to access the relevant box, where you
can &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh&lt;/code&gt; in, and do the basics (change the password, check your quota usage, move
files, create directories, etc.).&lt;/p&gt;

&lt;p&gt;I opted for a geographically distributed plan (it’s slightly more expensive, but it
is the primary backup method I’m using) and the lowest plan — the amount of
data is tiny as it’s mostly text files.&lt;/p&gt;

&lt;p&gt;And that’s essentially it. I paid for a year so I’ll be reminded about it next year
to go about renewing it.&lt;/p&gt;

&lt;h2 id=&quot;backup&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backup&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/meskyanichi/backup&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backup&lt;/code&gt; Ruby gem&lt;/a&gt; gives you a command line tool which will help generate 
the models and run them. But you should just install it as a gem, rather than using
Bundler or anything else.&lt;/p&gt;

&lt;p&gt;I did all of this under a specific &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backup&lt;/code&gt; user. This is configured to allow it to
use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tar&lt;/code&gt; through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo&lt;/code&gt; without asking for a password and not much else. It expects
the backup models and configuration files to exist in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Backup/&lt;/code&gt;, so this seemed
the best approach.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gem &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;backup
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The documentation suggests using the model generator to get started and that’s
pretty much what I did:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;backup generate:model &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; base &lt;span class=&quot;nt&quot;&gt;--archives&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--storages&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;scp&apos;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--compressors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;gzip&apos;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--notifiers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mail&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will give a rather detailed and well commented example. I started with this
and stripped it down to the bare essentials. If you don’t have one already, it will
also create a template &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.rb&lt;/code&gt;, which will contain a similar set of examples.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.rb&lt;/code&gt; can be used to set defaults for the models, so I opted to fill this with
as much as possible.&lt;/p&gt;

&lt;p&gt;But, some Real World™ examples are much more useful:&lt;/p&gt;

&lt;h3 id=&quot;baserb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base.rb&lt;/code&gt;&lt;/h3&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;##&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Base: Basic Linux backup model.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Archives and compresses: /etc, /var/log, /home, /mail.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Uploads to rsync.net.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# $ backup perform -t base&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;##&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Backup&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Basic Linux Model&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# archive&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:etc&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;use_sudo&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/etc/&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:logs&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;use_sudo&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tar_options&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;--warning=no-file-changed&apos;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/var/log&apos;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:home&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;use_sudo&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/home&apos;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# don&apos;t backup up the backup data.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exclude&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/home/backup/Backup/.tmp/&apos;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:mail&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;use_sudo&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tar_options&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;--warning=no-file-changed&apos;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/var/mail&apos;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;c1&quot;&gt;# compressor&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;compress_with&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Gzip&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# storage&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;store_with&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SCP&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# notifier&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;notify_by&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Mail&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;archive&lt;/code&gt; blocks are just an abstration over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tar&lt;/code&gt;, so you can pass through
options. In this case, I’ve ignored file change warnings for areas which are likely
to not harmfully change whilst the backup is running.&lt;/p&gt;

&lt;p&gt;Both the storage and notifier lines assume the configuration has already been made.
If you didn’t have these in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.rb&lt;/code&gt;, it wouldn’t work and you’d need to expand
the line into a block.&lt;/p&gt;

&lt;h3 id=&quot;configrb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.rb&lt;/code&gt;&lt;/h3&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;##&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# SCP Storage Type Defaults&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;##&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Backup&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Storage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SCP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;defaults&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;username&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ip&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;port&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;22&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;path&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;~/server_name/&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;keep&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;##&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Notifier Defaults&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;##&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Backup&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Notifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Mail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;defaults&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mail&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from&lt;/span&gt;                 &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt;                   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;address&lt;/span&gt;              &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;smtp.gmail.com&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;port&lt;/span&gt;                 &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;587&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;domain&lt;/span&gt;               &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;user_name&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;password&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;authentication&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;plain&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;encryption&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:starttls&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# * * * * * * * * * * * * * * * * * * * *&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#        Do Not Edit Below Here.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# All Configuration Should Be Made Above.&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;##&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Load all models from the models directory.&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dirname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;config_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;models&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;*.rb&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;instance_eval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scp&lt;/code&gt; block contains the details for &lt;a href=&quot;http://www.rsync.net/&quot;&gt;Rsync.net&lt;/a&gt;. The mail defaults are
currently set to the values for Gmail’s SMTP server (you’ll need to fill in all of
the other relevant bits). By default, this will notify about all events (successful,
with warnings or a failure). I kept this like this for about two weeks to confirm
it was running correctly.&lt;/p&gt;

&lt;h3 id=&quot;aside-criticism&quot;&gt;Aside: Criticism&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;The configuration files would be more appropriately stored in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc&lt;/code&gt;, given it’s
 designed for Unix-like systems.&lt;/li&gt;
  &lt;li&gt;The timestamps are annoying. Unix timestamps or &lt;a href=&quot;https://en.wikipedia.org/wiki/ISO_8601&quot;&gt;ISO 8601&lt;/a&gt; is far 
 more appropriate than defining &lt;em&gt;another&lt;/em&gt; bloody date format.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;automation&quot;&gt;Automation&lt;/h2&gt;

&lt;p&gt;To automate it, I just have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crontab&lt;/code&gt; entry under the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backup&lt;/code&gt; user. This then
runs at 0130 every morning and emails me if necessary.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-cron&quot;&gt;30 01 * * * backup perform -t base,www &amp;gt;/dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(By default, it writes a lot to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stdout&lt;/code&gt;, I’d rather not fill up an unmonitored
inbox with successes…)&lt;/p&gt;

&lt;p&gt;I configured this about two weeks before writing up this blog post. It’s been working
well since then, and I’ve deployed a similar configuration across at least two other
boxes.&lt;/p&gt;

&lt;p&gt;I now need to investigate the &lt;a href=&quot;https://github.com/cramerdev/backup-cookbook&quot;&gt;Chef cookbook&lt;/a&gt; and modify it to work similarly to
this…&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Debian/Ubuntu: Dynamic MOTD</title>
        <link href="https://nickcharlton.net/posts/debian-ubuntu-dynamic-motd.html" />
        <id>https://nickcharlton.net/posts/debian-ubuntu-dynamic-motd.html</id>
        <published>Thu, 01 Aug 2013 00:00:00 +0000</published>
        <updated>Thu, 01 Aug 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;In doing some client work recently, I noticed that Ubuntu now has a dynamically
generated MOTD (Message of the Day) — the message shown on login, through SSH
or locally.&lt;/p&gt;

&lt;p&gt;It turns out that this has existed for a quite a while. It works by a hook in PAM
(Linux’s authentication system), that runs a set of scripts to generate the MOTD
which is then passed along to the client. (Before this, it was generated using a
cron job that ran every 10 or so minutes.) Because it is part of PAM (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pam_motd.so&lt;/code&gt;,
specifically) it also has worked with Debian since Squeeze.&lt;/p&gt;

&lt;p&gt;I had tried to do something similar before. Back in mid-2009, I &lt;a href=&quot;/posts/ssh-banner-debian.html&quot;&gt;posted something
about configuring the “SSH Banner”&lt;/a&gt;. I had tried to do something dynamic
before posting that, but without a cronjob it wasn’t possible to do and constantly
regenerating a text file for a not-often seen login banner seemed silly.&lt;/p&gt;

&lt;p&gt;Out of the box with Ubuntu, a set of provided scripts exist in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/update-motd.d/&lt;/code&gt;,
which are run in ascending order to produce a static &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/motd&lt;/code&gt; file. As the
functionality is handled by PAM, on Debian, we just need to create the directory
and populate with a set of scripts.&lt;/p&gt;

&lt;p&gt;Like most &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.d/&lt;/code&gt; style configurations, the files are executed in ascending
order, and so prefixing each script with 00-99 will arrange the order of the final
output. Also, the scripts could be in any language but these are mostly shell apart
from where it becomes complex enough to be better off in Python.&lt;/p&gt;

&lt;h2 id=&quot;final-result&quot;&gt;Final Result&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; _          _          _      _
| | ___   _| |__  _ __(_) ___| | __
| |/ / | | | &apos;_ \| &apos;__| |/ __| |/ /
|   &amp;lt;| |_| | |_) | |  | | (__|   &amp;lt;
|_|\_\\__,_|_.__/|_|  |_|\___|_|\_\

Welcome to Debian 7.0 (3.2.0-3-amd64).

System information as of Mon Jul 29 22:13:06 UTC 2013:

System load:  0.0       Memory usage: 20.0%
Usage of /:   1.6%      Swap usage:   0.0%
Local users:  1

34 updates to install.
0 are security updates.

No mail.
Last login: Fri Jul 26 12:05:11 2013 from localhost
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My goal here was to have something that could quickly tell me which machine I was
using and some specifics of it, an understanding of the current state and any
actions that should be taken. But, at the same time it should be as concise as is
practical and, importantly be fast to execute.&lt;/p&gt;

&lt;p&gt;Another addition I’ll make soon is to add configuration manager status, so in my
case it will show the Chef last run, environment and roles applied. (This will also
help remind me which boxes are using Chef before I change things which will
automatically be reverted.)&lt;/p&gt;

&lt;p&gt;The following is broadly based upon the version provided by Canonical in Ubuntu
12.04, which is Copyright 2009-2010 Canonical Ltd. and licensed under the GPL. And
so this lot is also&lt;sup id=&quot;fnref:licensetext&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:licensetext&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;h2 id=&quot;header-00-header&quot;&gt;Header (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;00-header&lt;/code&gt;)&lt;/h2&gt;

&lt;p&gt;The first part is the header. This comprises of the ASCII art hostname and the
“Welcome…” text. I’m dynamically generating the ASCII art using the short version
of the hostname, but you may wish to hard code it (obviously, this will need
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;figlet&lt;/code&gt; installed.)&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/sh&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    00-header - create the header of the MOTD&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    Copyright (c) 2013 Nick Charlton&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    Copyright (c) 2009-2010 Canonical Ltd.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    Authors: Nick Charlton &amp;lt;hello@nickcharlton.net&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#             Dustin Kirkland &amp;lt;kirkland@canonical.com&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    This program is free software; you can redistribute it and/or modify&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    it under the terms of the GNU General Public License as published by&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    the Free Software Foundation; either version 2 of the License, or&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    (at your option) any later version.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    This program is distributed in the hope that it will be useful,&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    GNU General Public License for more details.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    You should have received a copy of the GNU General Public License along&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    with this program; if not, write to the Free Software Foundation, Inc.,&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; /etc/lsb-release &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; /etc/lsb-release

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DISTRIB_DESCRIPTION&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-x&lt;/span&gt; /usr/bin/lsb_release &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# Fall back to using the very slow lsb_release utility&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;DISTRIB_DESCRIPTION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;lsb_release &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi

&lt;/span&gt;figlet &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;hostname&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;printf&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;printf&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Welcome to %s (%s).&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DISTRIB_DESCRIPTION&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;uname&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;printf&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;system-information-10-sysinfo&quot;&gt;System Information (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10-sysinfo&lt;/code&gt;)&lt;/h2&gt;

&lt;p&gt;This is slightly more complicated. Using a mix of standard utilities, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/proc&lt;/code&gt; and
some text parsing, it’s quite easy to assemble the system information section. As
I’m only targetting Debian/Ubuntu, it’s quite easy to get this working.&lt;/p&gt;

&lt;p&gt;In the original implementation, Canonical use their “Landscape” product to generate
the statistics which provides a bit more functionality than this does.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    10-sysinfo - generate the system information&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    Copyright (c) 2013 Nick Charlton&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    Authors: Nick Charlton &amp;lt;hello@nickcharlton.net&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    This program is free software; you can redistribute it and/or modify&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    it under the terms of the GNU General Public License as published by&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    the Free Software Foundation; either version 2 of the License, or&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    (at your option) any later version.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    This program is distributed in the hope that it will be useful,&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    GNU General Public License for more details.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    You should have received a copy of the GNU General Public License along&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    with this program; if not, write to the Free Software Foundation, Inc.,&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; /proc/loadavg | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print $1}&apos;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;root_usage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;df&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-h&lt;/span&gt; / | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/\// {print $(NF-1)}&apos;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;memory_usage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;free &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/Mem/ { printf(&quot;%3.1f%%&quot;, $3/$2*100) }&apos;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;swap_usage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;free &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/Swap/ { printf(&quot;%3.1f%%&quot;, $3/$2*100) }&apos;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;users&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;wc&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-w&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;System information as of: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$date&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo
printf&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;System load:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Memory usage:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$load&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$memory_usage&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;printf&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Usage on /:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Swap usage:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$root_usage&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$swap_usage&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;printf&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Local users:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$users&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;updates-20-updates&quot;&gt;Updates (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;20-updates&lt;/code&gt;)&lt;/h2&gt;

&lt;p&gt;This is much more complicated than the other sections. Canonical achieves this in
Ubuntu by using the same mechanism the GUI software updater works (the notifer,
not synaptic). This implements the same requirement, but using the upstream
dependency that — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python-apt&lt;/code&gt; — which allows us to interact with
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt&lt;/code&gt;’s internals.&lt;/p&gt;

&lt;p&gt;This is quite closely based upon the version in Ubuntu, but is simplified&lt;sup id=&quot;fnref:aptcheck&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:aptcheck&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!/usr/bin/python
#
#   20-updates - create the system updates section of the MOTD
#   Copyright (c) 2013 Nick Charlton
#
#   Authors: Nick Charlton &amp;lt;hello@nickcharlton.net&amp;gt;
#   Based upon prior work by Dustin Kirkland and Michael Vogt.
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License along
#   with this program; if not, write to the Free Software Foundation, Inc.,
#   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sys&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;subprocess&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;apt_pkg&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;DISTRO&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subprocess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Popen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;lsb_release&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;-c&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;-s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                          &lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subprocess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PIPE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;communicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OpNullProgress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&apos;&apos;&apos;apt progress handler which supresses any output.&apos;&apos;&apos;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;is_security_upgrade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&apos;&apos;&apos;
    Checks to see if a package comes from a DISTRO-security source.
    &apos;&apos;&apos;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;security_package_sources&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Ubuntu&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;%s-security&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DISTRO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                               &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Debian&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;%s-security&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DISTRO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;origin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;security_package_sources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;origin&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;origin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# init apt and config
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;apt_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# open the apt cache
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apt_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpNullProgress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;SystemError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Error: Opening the cache (%s)&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# setup a DepCache instance to interact with the repo
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;depcache&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apt_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DepCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# take into account apt policies
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;depcache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read_pinfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# initialise it
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;depcache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# give up if packages are broken
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;depcache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;broken_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Error: Broken packages exist.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# mark possible packages
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# run distro-upgrade
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;depcache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;upgrade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# reset if packages get marked as deleted -&amp;gt; we don&apos;t want to break anything
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;depcache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;del_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;depcache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# then a standard upgrade
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;depcache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;upgrade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;SystemError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Error: Couldn&apos;t mark the upgrade (%s)&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# run around the packages
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;upgrades&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;security_upgrades&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pkg&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;packages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;candidate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;depcache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_candidate_ver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_ver&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# skip packages not marked as upgraded/installed
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;depcache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;marked_install&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;depcache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;marked_upgrade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# increment the upgrade counter
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;upgrades&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# keep another count for security upgrades
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_security_upgrade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;candidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;security_upgrades&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# double check for security upgrades masked by another package
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apt_pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version_compare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ver_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ver_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_security_upgrade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;security_upgrades&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;%d updates to install.&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;upgrades&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;%d are security updates.&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;security_upgrades&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# leave a trailing blank line
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is reasonably well commented, if you wished to delve into the implementation,
but at a higher level it:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Opens up the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt&lt;/code&gt; cache.&lt;/li&gt;
  &lt;li&gt;Does the equivalent of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get dist-upgrade&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Then the equivalent of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get upgrade&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Counts the possible packages which are marked to be installed or upgraded.&lt;/li&gt;
  &lt;li&gt;Reports, closes the cache and exits.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It’s important to note that whilst it does get a list of packages, it doesn’t
commit any changes and so no state will change (and I’m pretty sure I didn’t do
anything stupid).&lt;/p&gt;

&lt;p&gt;I tested this on a Ubuntu 12.04 (Precise) install, Debian Squeeze and then Wheezy.
That’s enough for me to be confident that it works well enough, but I couldn’t test
all possible package and so it’s quite possible that the reported number will be
incorrect.&lt;/p&gt;

&lt;p&gt;Also, Ubuntu has the ability to show distro upgrades. I couldn’t work out how to
replicate this. It’s less important with Debian — the major releases are
hardly a common event.&lt;/p&gt;

&lt;p&gt;You could do something similar with just bash and some text processing:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apt-get &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; Debug::NoLocking&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true &lt;/span&gt;upgrade | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; ^Inst | &lt;span class=&quot;nb&quot;&gt;wc&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But, this would lose the ‘security updates’ section. It’s also slightly slower, my
Python came in at 0.732s, versus 0.762s on the shell command above. Not that that’s
a very significant difference.&lt;/p&gt;

&lt;h2 id=&quot;footer-99-footer&quot;&gt;Footer (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;99-footer&lt;/code&gt;)&lt;/h2&gt;

&lt;p&gt;The default behaviour of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;motd&lt;/code&gt; is to present a combination of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/motd&lt;/code&gt; (which is
regenerated on reboot) and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/motd.tail&lt;/code&gt; which is designed to append a sys admin
message or similar. This just replicates the original functionality:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/sh&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    99-footer - write the admin&apos;s footer to the MOTD&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    Copyright (c) 2013 Nick Charlton&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    Copyright (c) 2009-2010 Canonical Ltd.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    Authors: Nick Charlton &amp;lt;hello@nickcharlton.net&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#             Dustin Kirkland &amp;lt;kirkland@canonical.com&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    This program is free software; you can redistribute it and/or modify&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    it under the terms of the GNU General Public License as published by&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    the Free Software Foundation; either version 2 of the License, or&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    (at your option) any later version.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    This program is distributed in the hope that it will be useful,&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    GNU General Public License for more details.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    You should have received a copy of the GNU General Public License along&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    with this program; if not, write to the Free Software Foundation, Inc.,&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; /etc/motd.tail &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; /etc/motd.tail &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;configuration&quot;&gt;Configuration&lt;/h2&gt;

&lt;p&gt;All of these files (my filenames are in brackets around the section title) should
be placed in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/update-motd.d/&lt;/code&gt; and then made executable
(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod +x /etc/update-motd.d/&lt;/code&gt;). When you login, PAM will regenerate the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;motd&lt;/code&gt;
file, which on Debian is located in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/run/motd&lt;/code&gt;. The file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/motd&lt;/code&gt; is just a
symlink to this location.&lt;/p&gt;

&lt;p&gt;Finally, to replicate the manner in which Ubuntu presents the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;motd&lt;/code&gt;, you need to
set the following in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/ssh/sshd_config&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PrintMotd no
PrintLastLog yes

#Banner /etc/issue
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It seems conterintuitive, but PAM will post the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;motd&lt;/code&gt; anyway, so it should be
turned off here. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PrintLastLog&lt;/code&gt; gives you the last login host and time. Then, the
banner can be commented out because our dynamically generated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;motd&lt;/code&gt; includes the
same information.&lt;/p&gt;

&lt;p&gt;Now I just need to turn it into a Chef cookbook…&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:licensetext&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;It also, sadly, means often there’s more license text than actual
code. But, I know people will blindly copy/paste the contents of the text boxes
and it seems wrong to not include it. &lt;a href=&quot;#fnref:licensetext&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:aptcheck&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;To be more specific, this is based upon &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-check.py&lt;/code&gt;, which can be
found in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/lib/update-notifier/&lt;/code&gt; in recent Ubuntu releases. &lt;a href=&quot;#fnref:aptcheck&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</summary>
    </entry>
    
    <entry>
        <title>7 Agile Best Practices that You Don&apos;t Need to Follow</title>
        <link href="https://nickcharlton.net/posts/dont-go-overboard-with-agile.html" />
        <id>https://nickcharlton.net/posts/dont-go-overboard-with-agile.html</id>
        <published>Sun, 07 Jul 2013 00:00:00 +0000</published>
        <updated>Sun, 07 Jul 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://swreflections.blogspot.co.uk/2013/05/7-agile-best-practices-that-you-dont.html&quot;&gt;Jim Bird pulls apart some of the bits of Agile&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Unlike most articles on the human side of software development, this brings up
some interesting research on agile principles In the Real World™.&lt;/p&gt;

&lt;p&gt;Notably:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Tests first or tests after isn’t important. Just that they’re there.&lt;/li&gt;
  &lt;li&gt;Pair programming as a default option seems to be counterproductive.&lt;/li&gt;
  &lt;li&gt;Remote pair programming (across locations, cultures, etc) doesn’t seem to work
at all.&lt;/li&gt;
  &lt;li&gt;But, as a tool for certain situations (bring in new developers to a codebase or
solving a specific problem, for example) it works rather well.&lt;/li&gt;
  &lt;li&gt;Daily stand up meetings a bit pointless taken as a constant.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Of course, like a tools, agile can look just like a fancy hammer.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://statuscode.org/&quot;&gt;via Status Code&lt;/a&gt;&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Multiple Displays (and OpenGL)</title>
        <link href="https://nickcharlton.net/posts/multiple-displays-opengl.html" />
        <id>https://nickcharlton.net/posts/multiple-displays-opengl.html</id>
        <published>Sat, 06 Jul 2013 00:00:00 +0000</published>
        <updated>Sat, 06 Jul 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Something I’ve not seen anyone comment on is the reasoning behind why Apple have
put off Multiple Displays — like the way Mavericks implements them — for
so long.&lt;/p&gt;

&lt;p&gt;My hunch is OpenGL. Apple has a custom implementation of OpenGL which extends well
into the way in which Cocoa and Cocoa Touch are implemented. On the Mac and on iOS
everything is OpenGL. Mavericks &lt;em&gt;finally&lt;/em&gt; brings us OpenGL 4.1, thus bringing the
Mac up to speed with both the Linux and Windows implementations.&lt;/p&gt;

&lt;p&gt;I assume that this was part of the reason. I imagine Mavericks brings us a much
improved graphics stack and that this significant change (which bought in OpenGL 4.1)
is also the reason why Apple decided to make a big change to multiple display
support.&lt;/p&gt;

&lt;p&gt;Of course, all of this is because of the impending new Mac Pro — there’s
little point shipping a cool GPU configuration if we’re still writing graphics code
from 2009.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://eggfreckles.net/notes/multiple-displays/&quot;&gt;But, as Thomas Brand says:&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;How long we have waited.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Indeed.&lt;/p&gt;

&lt;p&gt;Regarding the new Mac Pro, I, &lt;a href=&quot;http://kickingbear.com/blog/archives/349&quot;&gt;like Guy English, can also see cool new applications
of the hardware configuration&lt;/a&gt;.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>What&apos;s the mobile app market up to, then?</title>
        <link href="https://nickcharlton.net/posts/mobile-app-market.html" />
        <id>https://nickcharlton.net/posts/mobile-app-market.html</id>
        <published>Sat, 06 Jul 2013 00:00:00 +0000</published>
        <updated>Sat, 06 Jul 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://blog.securemacprogramming.com/2013/07/whats-the-mobile-app-market-up-to-then/&quot;&gt;Graham Lee talks about the way the app market has ended up going&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’ve seen his conclusion myself. The leads I’ve had recently have been for either 
building a specific user experience, or an interface to a wider system through some 
sort of API. The thing is, these projects are far, far more interesting than the 
crap that proceeded it.&lt;/p&gt;

&lt;p&gt;But, it’s not necessarily good for those of us who wish we had a more early-2000’s 
Mac software environment — the likes where &lt;a href=&quot;http://panic.com&quot;&gt;Panic&lt;/a&gt;, &lt;a href=&quot;http://www.red-sweater.com&quot;&gt;Red Sweater Software&lt;/a&gt; 
and many more can shine through with dedicated end-user customers.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>How Stripe Buids Software: Interview with Greg Brockman</title>
        <link href="https://nickcharlton.net/posts/interview-with-greg-brockman.html" />
        <id>https://nickcharlton.net/posts/interview-with-greg-brockman.html</id>
        <published>Sat, 06 Jul 2013 00:00:00 +0000</published>
        <updated>Sat, 06 Jul 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://blog.airbrake.io/devops/how-stripe-builds-software-an-interview-with-cto-greg-brockman/&quot;&gt;This interview with Greg Brockman of Stripe is quite interesting.&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It touches on the way they manage their development teams at Stripe, breaking into 
teams for parts of the different projects with one of the programmers leading. It’s 
a good demonstration of how you can function a company around the way Open Source 
projects are run and using various different tools (in this case &lt;a href=&quot;https://github.com/&quot;&gt;GitHub&lt;/a&gt;, &lt;a href=&quot;http://asana.com/&quot;&gt;Asana&lt;/a&gt;
, etc) to glue things together.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://statuscode.org/&quot;&gt;via Status Code&lt;/a&gt;.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>The Makers of Things</title>
        <link href="https://nickcharlton.net/posts/makers-of-things.html" />
        <id>https://nickcharlton.net/posts/makers-of-things.html</id>
        <published>Fri, 05 Jul 2013 00:00:00 +0000</published>
        <updated>Fri, 05 Jul 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://www.themakersofthings.co.uk/&quot;&gt;Anne Hollowday put together this fantastic series about members of the Society
for Model and Experimental Engineers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You should watch it.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Experiments with Android, a IOIO board and Heart Rate Monitoring</title>
        <link href="https://nickcharlton.net/posts/android-ioio.html" />
        <id>https://nickcharlton.net/posts/android-ioio.html</id>
        <published>Thu, 06 Jun 2013 00:00:00 +0000</published>
        <updated>Thu, 06 Jun 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;A few weeks back now, I worked on a project for &lt;a href=&quot;http://www.i-dat.org&quot;&gt;iDAT&lt;/a&gt;. I came in at the end to
fix a few bugs on a Android/hardware integration project. The idea was to use an
Android phone, along with a &lt;a href=&quot;https://github.com/ytai/ioio/wiki&quot;&gt;IOIO&lt;/a&gt; board and a &lt;a href=&quot;https://www.sparkfun.com/products/8661&quot;&gt;Polar heart rate monitor breakout
board&lt;/a&gt; to collect data on a person’s movements, heart rate (and a few other 
things).&lt;/p&gt;

&lt;p&gt;This was split into two distinct sections; the first was the hardware itself and
secondly the software which would poll data from the IOIO board and do something to
visualise it.&lt;/p&gt;

&lt;p&gt;For the hardware, I started off first trying to get some data from the HMRI (the
shorter name of the breakout board) using an Arduino. This worked quite quickly.
The board uses I²C to communicate and so this is only a few pins. Whilst 
&lt;a href=&quot;http://danjuliodesigns.com/sparkfun/hrmi.html&quot;&gt;Dan Julio&lt;/a&gt; provides some example code for the Arduino, this was a bit old and so
I needed to update this. You can find the updated code in my GitHub project,
&lt;a href=&quot;https://github.com/nickcharlton/hmri_arduino&quot;&gt;hmri_arduino&lt;/a&gt;. It should work fine with Arduino 1.0 and greater.&lt;/p&gt;

&lt;p&gt;To interface the HMRI with the IOIO board, the process was much the same as the
Arudino, but I needed to provide two 4.7kΩ pull up resistors to get a signal 
(the Arduino provides these for you). I also needed to desolder the SJ1 contacts and
solder the OP0 contacts for an I²C connection, a section from the manual is
listed below.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/hmri_schematic.png&quot; alt=&quot;I&amp;sup2;C Schematic from the manual&quot; /&gt;
  &lt;figcaption&gt;Figure 1: I&amp;sup2;C Schematic from the manual&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;After this, I needed to interface the HMRI, through the IOIO board on the Android
device. The IOIO project provides a library to do this — the interface is
Arduino inspired — and once you get the correct version, it’s quite easy to
get working.&lt;/p&gt;

&lt;p&gt;I was using a Samsung Galaxy SII (with a slightly older version of Android) and the
older style IOIO board. I matched up the board type with the current release as
listed on the &lt;a href=&quot;https://github.com/ytai/ioio/wiki/Downloads&quot;&gt;IOIO downloads page&lt;/a&gt;. This ended up stumbling me for quite a while;
the board refuses to connect without precisely the right firmware/library 
combination.&lt;/p&gt;

&lt;p&gt;For the implementation itself, I used a &lt;a href=&quot;http://en.wikipedia.org/wiki/Singleton_pattern&quot;&gt;singleton&lt;/a&gt;&lt;sup id=&quot;fnref:antipattern&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:antipattern&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; which is 
started up with the main activity and is then shutdown on completion. The idea behind 
this was to create a connection to the hardware and keep one thread which would 
handleit (the singleton starts a thread for interacting with the  hardware, and then 
provides a thread-safe way in which to interface with it). Sadly, this didn’t quite 
work with the way Android is designed and so doesn’t quite work how I had expected. 
Instead, the hardware connection is started and stopped when the main activity 
run-loop is either started or stopped. Due to the way Android implements it’s 
run-loop this is all that is possible (coming from predominantly iOS, this such an 
odd implementation and quite annoying).&lt;/p&gt;

&lt;p&gt;I never resolved the issue before shipping; as far as I understand, it’s impossible 
to fully resolve it because of Android’s stupid implementation.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update:&lt;/strong&gt; Ytai Ben-Tsvi (the developer behind IOIO) emailed me to say that I should
have been using Android’s “Services” functionality, which I didn’t know existed.
This would solve all of my state issues.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And finally, the code for &lt;a href=&quot;https://github.com/i-DAT/Bio-OS-Android&quot;&gt;the whole project is up on GitHub&lt;/a&gt; (which should
give you the best way to see it in action).&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:antipattern&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Many consider the Singleton to be a programming anti-pattern, but
I disagree for situations such as this. Yes, it does introduce global scope,
but when you’re accessing hardware, or a data model (where I’ve used singletons
before) it works quite well. You only want to instantiate it once, anyway. In
the case of hardware it’s preferable to keep the same connection open. &lt;a href=&quot;#fnref:antipattern&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</summary>
    </entry>
    
    <entry>
        <title>Side Projects</title>
        <link href="https://nickcharlton.net/posts/side-projects.html" />
        <id>https://nickcharlton.net/posts/side-projects.html</id>
        <published>Tue, 04 Jun 2013 00:00:00 +0000</published>
        <updated>Tue, 04 Jun 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Much to my chagrin about Medium in general, there’s been some nice posts recently. 
This set on Side Projects fits exactly where I feel about them;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/what-i-learned-building/a8d1fec5379d&quot;&gt;Lee Munroe talks about what he’s learned with doing side projects&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/freelancers-life/a07e211240b2&quot;&gt;Ximena Vengoechea talks about them being the “new resume”&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I like this idea, and as I redo mine (I’ve technically graduated now&lt;sup id=&quot;fnref:job&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:job&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;) I’m 
adjusting mine to focus on these equally with everything else.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:job&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;I’m not looking for a job, as I’m intending on doing a Masters. However, I’m 
open to taking on freelance projects, if you have them. &lt;a href=&quot;#fnref:job&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</summary>
    </entry>
    
    <entry>
        <title>Wired writes something sensible about the Internet of Things</title>
        <link href="https://nickcharlton.net/posts/wired-io.html" />
        <id>https://nickcharlton.net/posts/wired-io.html</id>
        <published>Tue, 21 May 2013 00:00:00 +0000</published>
        <updated>Tue, 21 May 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://www.wired.com/gadgetlab/2013/05/internet-of-things/all/&quot;&gt;Bill Wasik writes something about IoT over on Wired.&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is probably the first post I’ve seen at a more mainstream publication which
talks about IoT well and doesn’t get it all wrong.&lt;/p&gt;

&lt;p&gt;I also quite like that they use the term, “Sensor Revolution”. We’re mostly talking 
about adding a layer of sensors to objects, not giving a fridge a web browser.&lt;/p&gt;

&lt;p&gt;And, the location stuff reminds me of &lt;a href=&quot;http://www.meridianapps.com/&quot;&gt;Meridian&lt;/a&gt;, and some of the research I’m 
interested in (but my stuff is attached to robots).&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>You should write about yourself more</title>
        <link href="https://nickcharlton.net/posts/personal-brands.html" />
        <id>https://nickcharlton.net/posts/personal-brands.html</id>
        <published>Tue, 21 May 2013 00:00:00 +0000</published>
        <updated>Tue, 21 May 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://tommorris.org/posts/8268&quot;&gt;Tom Morris talks about writing about yourself on your blog&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“As for one’s “personal brand”? Puh-lease. The concept of such a thing is so toxic 
to human honesty as to make me want to kick a small child. The reason we’ve only 
now seen one professional sportsman in the United States come out of the closet as 
gay is precisely because of “personal brand”. We’re all too horrified about 
offending some hypothetical future employer with our opinions or even our very 
own lives that we hide away from telling anyone anything. Sorry, but I’m with 
the kids on this: YOLO. Lives are short, closets are bad, crippling fear is 
toxic. If your personal brand demands that you live your life in fear of 
disclosing important parts of your life or your experience, the answer is to 
reject the whole sodding concept of personal brands.”&lt;/p&gt;
&lt;/blockquote&gt;

</summary>
    </entry>
    
    <entry>
        <title>George Zarkadakis: Love and artificial intelligence</title>
        <link href="https://nickcharlton.net/posts/love-and-artificial-intelligence.html" />
        <id>https://nickcharlton.net/posts/love-and-artificial-intelligence.html</id>
        <published>Tue, 21 May 2013 00:00:00 +0000</published>
        <updated>Tue, 21 May 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://www.aeonmagazine.com/being-human/george-zarkadakis-love-artificial-intelligence/&quot;&gt;The essayist George Zarkadakis writes about love and AI&lt;/a&gt;.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>The downsides of live music</title>
        <link href="https://nickcharlton.net/posts/downsides-of-live-music.html" />
        <id>https://nickcharlton.net/posts/downsides-of-live-music.html</id>
        <published>Tue, 21 May 2013 00:00:00 +0000</published>
        <updated>Tue, 21 May 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://therealadam.com/2013/05/18/the-downsides-of-live-music/&quot;&gt;Adam Keys on Live Music&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All of this, basically. I love the &lt;em&gt;idea&lt;/em&gt; of live music, but so often it turns out 
to not be quite as a good as you imagine it to be. And, much like Keys, I listen to 
albums in a long-form way. Sometimes a whole artist’s discography in an evening 
(and for some, over a day). I much prefer it that way.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>The Default Narative</title>
        <link href="https://nickcharlton.net/posts/default-narrative.html" />
        <id>https://nickcharlton.net/posts/default-narrative.html</id>
        <published>Tue, 21 May 2013 00:00:00 +0000</published>
        <updated>Tue, 21 May 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://tracks.ranea.org/post/47791661856/the-default-narrative&quot;&gt;Watts Martin talks about the “default narrative” which pervades journalism&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Basically this. I don’t read most technology news any more because it’s almost 
always scraping the bottom of the barrel crap. And bar one or two writers, this 
default way of thinking is all over Apple “journalism”, too.&lt;/p&gt;

&lt;p&gt;It stinks.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>SparkFun: How to Build a Building</title>
        <link href="https://nickcharlton.net/posts/how-to-build-a-building.html" />
        <id>https://nickcharlton.net/posts/how-to-build-a-building.html</id>
        <published>Tue, 07 May 2013 00:00:00 +0000</published>
        <updated>Tue, 07 May 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;https://www.sparkfun.com/news/1122&quot;&gt;Nate Seidle of SparkFun talks about their upcoming building&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’ve — suprisingly — seen a few building designs over the years, and 
they’re always cool to see. When I was on &lt;a href=&quot;/posts/finishing-at-rokk-media.html&quot;&gt;placement&lt;/a&gt;, we moved to a new office
building and had to plan out all of the data drops on both floors, and today I
saw a bundle of architect’s plans for an interesting project in Plymouth (that,
obviously, I can’t talk about.)&lt;/p&gt;

&lt;p&gt;But, what’s very cool to see here is SparkFun openly documenting it all. Much of
this stuff gets lost in folders of paperwork after the project is completed and
all of the contractors move on.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>The Way of the Megapode</title>
        <link href="https://nickcharlton.net/posts/way-of-the-megapode.html" />
        <id>https://nickcharlton.net/posts/way-of-the-megapode.html</id>
        <published>Mon, 06 May 2013 00:00:00 +0000</published>
        <updated>Mon, 06 May 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://www.rousette.org.uk/blog/archives/the-way-of-the-megapode/&quot;&gt;But She’s a Girl talks about Douglas Adams, endangered species and, er, scripting&lt;/a&gt;.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Space Apps Challenge 2013</title>
        <link href="https://nickcharlton.net/posts/space-apps-challenge-2013.html" />
        <id>https://nickcharlton.net/posts/space-apps-challenge-2013.html</id>
        <published>Mon, 06 May 2013 00:00:00 +0000</published>
        <updated>Mon, 06 May 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;A few weekends ago was the second &lt;a href=&quot;http://spaceappschallenge.org/&quot;&gt;NASA Space Apps Challenge&lt;/a&gt;, a hack day centred
around building stuff related to space and worldwide collaboration. The
&lt;a href=&quot;/posts/nasa-space-apps-challenge-predict-the-sky.html&quot;&gt;last one was fantastic&lt;/a&gt; and this one also did not disappoint. This time 
around, we set around to continue working on Predict the Sky, but more of that in a 
bit.&lt;/p&gt;

&lt;p&gt;For me, it worked out as a nice breather before the mad rush of &lt;a href=&quot;/posts/final-year-project-over.html&quot;&gt;finishing off my
project&lt;/a&gt; (even though I did spend some time reviewing some code and
adjusting a few things).&lt;/p&gt;

&lt;p&gt;But, more interesting was seeing what other people were coming up with. As an
offshoot of &lt;a href=&quot;http://www.growers-nation.org/&quot;&gt;Growers Nation&lt;/a&gt; (er, pun not intended) we had an open hardware soil
analyser, entitled “&lt;a href=&quot;http://spaceappschallenge.org/project/mudpi&quot;&gt;MudPi&lt;/a&gt;”, which is able to collect humidity, temperature,
dew point &amp;amp; moisture and is designed to be placed in the soil somewhere. They were
commended for being quite close to market. I’ll be interested to see what comes
next out of it.&lt;/p&gt;

&lt;p&gt;Next, was a collection of projects from mostly &lt;a href=&quot;http://plymouth.ac.uk/&quot;&gt;Plymouth University&lt;/a&gt; students
entitled &lt;a href=&quot;http://spaceappschallenge.org/project/arduhack/&quot;&gt;ArduHack&lt;/a&gt; that was focused around the &lt;a href=&quot;http://www.kickstarter.com/projects/575960623/ardusat-your-arduino-experiment-in-space&quot;&gt;ArduSat&lt;/a&gt; platform. This is an
&lt;a href=&quot;http://arduino.cc/&quot;&gt;Arduino&lt;/a&gt; based &lt;a href=&quot;http://www.cubesat.org/&quot;&gt;CubeSat&lt;/a&gt;, which itself is a project with the aim to reduce the
cost of getting satellites into space — to the point where groups of people,
researchers (as in, not space ones) can do. Anyway, half of the team was focused
upon attaching an earth orientated camera to the ArduSat, so that people would be
able to photograph themselves from space; the key bit here was being able to
photography themselves — people could essentially operate their own spy
satellite for a moment in time.&lt;/p&gt;

&lt;p&gt;The other half of the ArduSat project was about bringing some of the sensors “back
down to earth”. Using a combination of a &lt;a href=&quot;https://www.sparkfun.com/products/10825&quot;&gt;Magician Robot Kit&lt;/a&gt;, an Arduino, 
&lt;a href=&quot;http://www.raspberrypi.org/&quot;&gt;Raspberry Pi&lt;/a&gt; and a &lt;a href=&quot;http://www.ti.com/tool/cc2541dk-sensor&quot;&gt;TI SensorTag Development Kit&lt;/a&gt;. The key bit is the last one,
it includes an IR temperature sensor, a gyroscope, accelerometer, magnetometer,
pressure sensor and a humidity sensor all of which are able to communicate over the
&lt;a href=&quot;http://en.wikipedia.org/wiki/Bluetooth_low_energy&quot;&gt;Bluetooth Low Energy&lt;/a&gt; standard. From here, they used the Raspberry Pi to
communicate with the SensorTag, to then control the Arduino which would drive the
robot around.&lt;/p&gt;

&lt;p&gt;Finally, the team behind &lt;a href=&quot;http://spaceappschallenge.org/project/webrover1/&quot;&gt;WebRover1&lt;/a&gt; where looking at expanding what a set of
&lt;a href=&quot;http://mindstorms.lego.com/&quot;&gt;Lego Mindstorms&lt;/a&gt; based robots could do for outreach — getting young people
interested in robotics and it’s related &lt;a href=&quot;http://en.wikipedia.org/wiki/STEM_fields&quot;&gt;STEM&lt;/a&gt; subjects. They ended up with new
control code and an easy to use front end which would work with most browsers
(aimed a touch devices). Their project page gives a better explanation of the user
interface design process.&lt;/p&gt;

&lt;p&gt;There were several other interesting things by other groups there, too. And, the last 
two, &lt;a href=&quot;http://spaceappschallenge.org/project/arduhack/&quot;&gt;ArduHack&lt;/a&gt; and &lt;a href=&quot;http://spaceappschallenge.org/project/webrover1/&quot;&gt;WebRover1&lt;/a&gt; are up for global judging.&lt;/p&gt;

&lt;h2 id=&quot;a-continuation-of-predict-the-sky&quot;&gt;A Continuation of Predict the Sky&lt;/h2&gt;

&lt;p&gt;A few weeks before the event, I’d emailed around asking to see if any of the old
team were interested in spending the weeknd continuing on with Predict the Sky. My
hope was to catch up with the work which was done at the Met Office’s Weather for
Fun event last year (which I’d missed) and for us to work out where we’d take the
project. Personally, I wanted a documented API that we could wave around at people
and then from there work on the mobile applications and so forth that we had worked
on at the first Space Apps Challenge.&lt;/p&gt;

&lt;p&gt;And actually; that’s exactly what we did. &lt;a href=&quot;https://twitter.com/ehibling&quot;&gt;Emma&lt;/a&gt; was around all weekend, &lt;a href=&quot;http://www.sophiedennis.co.uk/&quot;&gt;Sophie&lt;/a&gt;
was with the &lt;a href=&quot;http://spaceappschallenge.org/project/webrover1/&quot;&gt;WebRover1&lt;/a&gt; team (but we grabbed her on a few bits and pieces) and we
were joined by a few more people.&lt;/p&gt;

&lt;p&gt;In the end, we ended up assembling &lt;a href=&quot;http://predictthesky.org/&quot;&gt;predictthesky.org&lt;/a&gt;, which will contain a
description of what the project is all about, the people involved, the API
documentation (there’s some examples at the moment, they need actually implementing)
and eventually some cool projects that are using it.&lt;/p&gt;

&lt;p&gt;We also looked at the way we were going about calculations and the data sources for
certain objects. We’ll be using &lt;a href=&quot;http://rhodesmill.org/pyephem/&quot;&gt;PyEphem&lt;/a&gt; for much of it and relying on &lt;a href=&quot;https://www.space-track.org/&quot;&gt;Space Track&lt;/a&gt;
for the object references (a project run under contract to the US Department of
Defence). A few other data sources will be used for other objects, too.&lt;/p&gt;

&lt;p&gt;The whole API will eventually be implemented using &lt;a href=&quot;http://flask.pocoo.org/&quot;&gt;Flask&lt;/a&gt; and you can find the
code (and source for the GitHub Pages based site) under the &lt;a href=&quot;https://github.com/PredictTheSky&quot;&gt;Predict the Sky 
Organisation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My next steps after I finish University (rather soon, now) is to start on the code
side, and also build out the documentation — especially for others to
contribute who are new to the project.&lt;/p&gt;

&lt;p&gt;In the mean time, if you’re interested, &lt;a href=&quot;/about&quot;&gt;shout at me&lt;/a&gt; and I’ll make sure
something is done about it.&lt;/p&gt;

&lt;p&gt;But overall, another great event, one of hopefully many more to come.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Maker Faire 2013</title>
        <link href="https://nickcharlton.net/posts/maker-faire-2013.html" />
        <id>https://nickcharlton.net/posts/maker-faire-2013.html</id>
        <published>Mon, 06 May 2013 00:00:00 +0000</published>
        <updated>Mon, 06 May 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Last weekend was &lt;a href=&quot;http://makerfaireuk.com/&quot;&gt;Maker Faire UK&lt;/a&gt;, up in Newcastle. &lt;a href=&quot;http://www.stewartstarbuck.co.uk/&quot;&gt;Stewart&lt;/a&gt;, &lt;a href=&quot;http://danbjorn.subvert.org.uk/&quot;&gt;Dan&lt;/a&gt; and I
headed up (aided by Stewart’s Dad’s superb driving skills), part of us from Exeter
and Dan from Nottingham.&lt;/p&gt;

&lt;p&gt;My journey started on the 6:55 train out of Plymouth to get to Exeter for around 
8:00, from there we set off on the drive to Newcastle, via Nottingham. It’s quite 
the trip; we landed at the &lt;a href=&quot;http://www.life.org.uk/&quot;&gt;Life Centre&lt;/a&gt; by about 18:00 with just about enough
time to setup, grab something to eat, find the hotel and then crash in preparation
for Day 1 of the Maker Faire.&lt;/p&gt;

&lt;p&gt;Our plan for being there was two fold; one was to evangelise 3D printing (as in,
the stuff you can do on your desk) and secondly to help sell a few RepRap kits by
promoting &lt;a href=&quot;http://www.printedworlds.co.uk/&quot;&gt;Printed Worlds&lt;/a&gt;. To do this we bought along a RepRap and spent the
weekend printing off whistles. These make a cool demonstration as just off the
printer they essentially work (you just have to poke the ball that’s printed inside
of the body). We gave away a few to slobbery children (and, er, adults.) We’ll see
how successful it ends up being in the next few weeks&lt;sup id=&quot;fnref:buy&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:buy&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/maker-faire-danbjorn.jpg&quot; alt=&quot;Dan doing some evangelism.&quot; width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Dan doing some evangelism.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;For the evangelism side, this worked rather well. In the photo above you can see
Dan explaining the purpose of a printed part to a group of people (this is likely 
a hotend assembly, although it’s hard to tell.)&lt;/p&gt;

&lt;p&gt;And, as you can see below, we regularly had a rather huge group of people crowded
around. The working RepRap (which was printing those whistles) can be blamed for
much of it. It was a great crowd puller.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/maker-faire-crowd.jpg&quot; alt=&quot;The crowd around the stand on Day 2.&quot; width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;The crowd around the stand on Day 2.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Overall, we had an interesting mix of people come over to the stand. A small proportion
hadn’t heard all that much about 3D printing, had seen the RepRap going and came 
over to see what was going on. A much larger proportion had heard of 3D printing
and were subsequently rather impressed with what we were doing and indeed, impressed
with the price of the kit we were promoting. And a few others knew quite a bit
(these often had RepRap’s or MakerBots or one of the many others that exist) and
were more interested in us personally; that was pretty cool.&lt;/p&gt;

&lt;p&gt;Another thing that was nice to see was design and technology teachers talking about
what they were either doing, or thinking about doing with a 3D printer. This is
already something that Stewart has been doing with local schools and colleges around
Exeter; the students can work on their project in the morning (say, learning CAD)
and then by the afternoon print it and see it as a real object. As someone who was
quite uninspired after making the fifth wooden box at school, I can see this as
being quite revolutionary — especially on the motivation front.&lt;/p&gt;

&lt;p&gt;At the end of day one, we were fed and then head off in search of a pub. Firstly,
we ended up at Newcastle’s Brew Dog Pub (which was rather nice), but then I suggested
meeting up with &lt;a href=&quot;http://blog.amyl.org.uk&quot;&gt;Adam&lt;/a&gt;, who was in town, and a few other makers at a Weatherspoon’s 
closer to the venue. Here we ended up meeting &lt;a href=&quot;http://about.me/coldclimate&quot;&gt;Oli Wood&lt;/a&gt; and a bunch of other people. 
And had a fine time. Day 2 then followed, with slightly less, but still quite a lot
of people. After closing time, we packed up, found something to eat and then turned
in. We were leaving at 7, after all.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/maker-faire-dancing-robots.jpg&quot; alt=&quot;Pole Dancing Robots. Photo courtesy of Oli Wood.&quot; width=&quot;500px&quot; /&gt;
  &lt;figcaption&gt;Pole Dancing Robots. Photo courtesy of 
      &lt;a href=&quot;https://twitter.com/coldclimate/status/328102769036767233/photo/1&quot;&gt;Oli Wood&lt;/a&gt;.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;As a “maker” myself, it was fascinating seeing everyone else’s creations. From
&lt;a href=&quot;http://pancakebot.com/&quot;&gt;Pancake Bot&lt;/a&gt; to the Pole Dancing Robots in the photo above, there were lots of
things to see. There was also a good showing from the many &lt;a href=&quot;http://hackspace.org.uk/view/Main_Page&quot;&gt;Hack Spaces&lt;/a&gt; around the
country; notably &lt;a href=&quot;http://nottinghack.org.uk/&quot;&gt;Nottingham&lt;/a&gt; who had a huge stand.&lt;/p&gt;

&lt;p&gt;Exhibiting at Maker Faire, and the faire itself was great fun. I hope to be at the
next one. But in the mean time, I’ll likely next be at the &lt;a href=&quot;http://www.makerfairebrighton.com&quot;&gt;Brighton Mini Maker 
Faire&lt;/a&gt; in September.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:buy&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;And if you’re interested, go and buy a kit. Say I sent you. &lt;a href=&quot;#fnref:buy&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</summary>
    </entry>
    
    <entry>
        <title>Building the Bomber Cam with 3D Printing and Scraps</title>
        <link href="https://nickcharlton.net/posts/bomber-cam.html" />
        <id>https://nickcharlton.net/posts/bomber-cam.html</id>
        <published>Mon, 06 May 2013 00:00:00 +0000</published>
        <updated>Mon, 06 May 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://www.polygonsandwich.com/blog/2013/5/3/building-the-bomber-cam&quot;&gt;A 3D printed lens mounting for a WW2 slightly-radioactive bomber lens&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I find these kind of applications of 3D printing fascinating. Given something cool,
what can someone do to make it useful with the application of a 3D printer?&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Paul Miller on his return to the Internet</title>
        <link href="https://nickcharlton.net/posts/paul-miller-returns.html" />
        <id>https://nickcharlton.net/posts/paul-miller-returns.html</id>
        <published>Thu, 02 May 2013 00:00:00 +0000</published>
        <updated>Thu, 02 May 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://www.theverge.com/2013/5/1/4279674/im-still-here-back-online-after-a-year-without-the-internet&quot;&gt;Paul Miller writes about his return to the internet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I don’t usually read tech news, but I have been following along Paul Miller’s
departure from the internet. It’s interesting, like Miller, I like long form articles
— especially writing them — and for a long time I’ve attempted to get
through the mountain of books (plus plenty more awaiting purchase in my Amazon
basket) that litter every flat surface around me.&lt;/p&gt;

&lt;p&gt;But it never happens. I get caught up with everything else. The whole concept of
“work-life balance” is alien to me and instead I attempt to balance a stack of plates
many times my height. And when they fall (they always do — there’s always a
sudden deadline) I succeed in only catching the stuff that’s closer to me —
missing the long term bits I want to be working on.&lt;/p&gt;

&lt;p&gt;Of course, I suspect I’d come to the same conclusion as Miller if I attempted
something as drastic as this. The internet is where the people are, and I’d miss
them.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Final Year Project is Over</title>
        <link href="https://nickcharlton.net/posts/final-year-project-over.html" />
        <id>https://nickcharlton.net/posts/final-year-project-over.html</id>
        <published>Tue, 30 Apr 2013 00:00:00 +0000</published>
        <updated>Tue, 30 Apr 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;And so, my final year project was handed in. This marks the tail end of my degree
— I’ve only got a poster to assemble, a demo to do and three exams left. It’s
by far been the hardest thing I’ve ever done, but certainly the most rewarding.&lt;/p&gt;

&lt;p&gt;The work I undertook for my final year project was to investigate the use of Kalman
filters for &lt;a href=&quot;http://en.wikipedia.org/wiki/Simultaneous_localization_and_mapping&quot;&gt;robot localisation and mapping&lt;/a&gt;. That wasn’t quite my initial plan, 
though. That was to build a Quadrotor, then write the code for the localisation and
mapping, followed by running a series of tests to see how well it worked. Sadly, I 
ran out of time and got just past having ¾ of a Quadrotor prototype and a
simple 2D simulator which just about demonstrates the thing I was attempting to.&lt;/p&gt;

&lt;p&gt;But that’s fine as I did end up with doing quite a lot of working and learning a
hell of a lot more than I could have expected.&lt;/p&gt;

&lt;p&gt;I also have quite a few plans from where I intend to take the project — mostly
the hardware and electronics side — and I’ll be slowly revealing the project
as it is and then what I intend to do over time.&lt;/p&gt;

&lt;p&gt;For the past few months this has been almost completely consuming. I look forward
to finally coming up for some air at &lt;a href=&quot;http://www.makerfaireuk.com&quot;&gt;Maker Faire UK&lt;/a&gt; this weekend.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Are we approaching a new AI winter?</title>
        <link href="https://nickcharlton.net/posts/ai-winter.html" />
        <id>https://nickcharlton.net/posts/ai-winter.html</id>
        <published>Mon, 08 Apr 2013 00:00:00 +0000</published>
        <updated>Mon, 08 Apr 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://alanwinfield.blogspot.com/2013/03/a-crisis-of-expectations.html&quot;&gt;Alan Winfield presents a short paper on the likelihood of a pending AI winter&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“We need to be truthful about the limitations of robots and robot intelligence, 
and measured with our predictions. We can show that real robots are both very 
different and much more surprising than their fictional counterparts”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Of course, articles such as &lt;a href=&quot;http://www.guardian.co.uk/commentisfree/2013/mar/29/domestic-drones-unique-dangers&quot;&gt;this by Glenn Greenwald hardly help&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“the most sophisticated robotics use artificial intelligence that [can] seek out 
and record certain kinds of suspicious activity”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(Although, I do agree on the privacy, et. al. risks of using surveillance tools 
such as ‘drones’ in the name of “the public good”.)&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Expectations</title>
        <link href="https://nickcharlton.net/posts/expectations.html" />
        <id>https://nickcharlton.net/posts/expectations.html</id>
        <published>Fri, 05 Apr 2013 00:00:00 +0000</published>
        <updated>Fri, 05 Apr 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://canadian-fury.com//2013/04/04/expectations/&quot;&gt;Doug Stephen talks about Expectations, and a few other things&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Sounds familiar.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Test Environments with Vagrant and Chef</title>
        <link href="https://nickcharlton.net/posts/test-environments-with-vagrant-and-chef.html" />
        <id>https://nickcharlton.net/posts/test-environments-with-vagrant-and-chef.html</id>
        <published>Thu, 04 Apr 2013 00:00:00 +0000</published>
        <updated>Thu, 04 Apr 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I recently completed a project for an assignment which had a mess of dependencies.
It depends upon &lt;a href=&quot;http://libgit2.github.com/&quot;&gt;libgit2&lt;/a&gt; and &lt;a href=&quot;http://www.pygit2.org/&quot;&gt;pygit2&lt;/a&gt;, both of which are a complete pain to 
get working. After it blew up in my face for the third time, I realised I was being 
silly and should go back to using &lt;a href=&quot;http://www.vagrantup.com/&quot;&gt;Vagrant&lt;/a&gt;, and along with it, the configuration 
management tool, &lt;a href=&quot;http://www.opscode.com/&quot;&gt;Chef&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Why Chef, over it’s main competitor, &lt;a href=&quot;https://puppetlabs.com/&quot;&gt;Puppet&lt;/a&gt;? I like it more. But I can’t really
describe why. I’m like that sometimes. I wish it were because one had much nicer,
more accessable documentation, but neither seems to be the case. Fortunately,
Vagrant has lovely documentation and so I’m not going to replicate it. I am going
to go into detail about how to use both together and provide an introduction to Chef,
though.&lt;/p&gt;

&lt;p&gt;Vagrant allows you to start, stop and destroy virtual machines in a nice abstraction
which also provides hooks to automate the provisioning of them. I want to be able to
quickly spin up a test environment per project and when it’s started I want everything
configured for me, &lt;a href=&quot;https://github.com/nickcharlton/dotfiles&quot;&gt;dotfiles&lt;/a&gt;, libraries, applications, shared folders and so on.&lt;/p&gt;

&lt;p&gt;Chef handles the second bit. It’s known as a configuration management tool. It
describes what we want the system to look like after it’s run, but we don’t care
about how we get there. Chef overuses the cooking analogy a bit, but: A recipe 
describes the way a specific tool is configured, along with any dependencies. A 
cookbook contains the recipes we wish to run on a system. I’m mostly going to talk
about using “Chef Solo”, that is, Chef without a server as that works best for
environments which are quickly created, used and thrown away.&lt;/p&gt;

&lt;h3 id=&quot;before-you-begin&quot;&gt;Before You Begin&lt;/h3&gt;

&lt;p&gt;Firstly, you should install:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.virtualbox.org/&quot;&gt;VirtualBox&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.vagrantup.com/&quot;&gt;Vagrant&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then you should at least skim read the &lt;a href=&quot;http://docs.vagrantup.com/&quot;&gt;Vagrant documentation&lt;/a&gt;. I’ll only describe
it lightly. By the end, I’ll have a “&lt;a href=&quot;https://github.com/nickcharlton/test-environment&quot;&gt;test-environment&lt;/a&gt;” repository which I’ll clone
whenever I need a new to spin something up.&lt;/p&gt;

&lt;h3 id=&quot;virtual-machines-with-vagrant&quot;&gt;Virtual Machines with Vagrant&lt;/h3&gt;

&lt;p&gt;Vagrant is the glue that holds all of this together and makes interacting with
virtual machines as pleasurable as possible. It uses a simple Ruby DSL&lt;sup id=&quot;fnref:ruby&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:ruby&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; to
describe the virtual machines that should be managed. All of this is stored in a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vagrantfile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Vagrant uses the notion of “boxes” to describe preconfigured base VMs for which we
can work from. I’m using Ubuntu here. You should also look at &lt;a href=&quot;http://www.morethanseven.net/&quot;&gt;Gareth Rushgrove&lt;/a&gt;’s
&lt;a href=&quot;http://vagrantbox.es/&quot;&gt;Vagrant Boxes&lt;/a&gt; site. A simple Vagrant friendly install with a copy of Chef is
all you need.&lt;/p&gt;

&lt;p&gt;Firstly, create a Vagrantfile and, if you don’t have one already, grab a box:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vagrant init
vagrant box add precise32 &lt;span class=&quot;se&quot;&gt;\ &lt;/span&gt;http://files.vagrantup.com/precise32.box
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then start the Vagrantfile (it already has an example configuration, but I prefer it
a little tidier):&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Vagrant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;box&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;precise32&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, you can start the virtual machine and ssh into it:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vagrant up
vagrant ssh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And, you can throw the box away by running:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vagrant destroy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This might be the bit where you wish you had an SSD, if you don’t already have one. 
You’ll do that a lot. This base box configures Ruby, Chef and a few other things for 
us. And notably it’ll share the working directory in which the Vagrantfile is 
contained. You should now be good to start thinking about provisioning.&lt;/p&gt;

&lt;h3 id=&quot;provisioning-with-chef&quot;&gt;Provisioning with Chef&lt;/h3&gt;

&lt;p&gt;There are a few versions of Chef. These can be broadly split into “client”, known as
“Solo” and “client-server”, in the case of “Chef Server”, “Chef Hosted” or “Chef
Private”. These latter three provide a central area in which to manage nodes, 
cookbooks, roles and so on.&lt;/p&gt;

&lt;p&gt;The node is a server (actually, it’s not necessarily a server, because you could 
use Chef to manage your workstation too) that is managed by Chef. The routine to
complete a task is known as a recipe. This is used to install, configure and start
services, from the appropriate package management system, or from source and using
the correct dependencies. These recipes are grouped together in a “cookbook”.&lt;/p&gt;

&lt;p&gt;A set of cookbooks may be configured together so that a node can have a “role”
applied to it. This is something like “web” or “database”, causing Chef to configure 
the appropriate cookbooks for it.&lt;/p&gt;

&lt;p&gt;“Data Bags” are collections of data (which may be encrypted) which are used by Chef
to aid the install. The idea here is that the data can be kept seperate from the
configuration, like usernames, passwords or keys. Below, I’m using this to put a
special set of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh&lt;/code&gt; keys in place so I can push code up to GitHub inside the test
environment.&lt;/p&gt;

&lt;p&gt;One of the few key points to Chef, and similar configuration management tools is 
that of “idempotence” — every time Chef is run, the same state will be had 
at the end. So, for example if you run Chef manually and everything is configured, 
nothing will happen. If you destroy this virtual machine and create another, once
Chef has done it’s thing, it’ll be exactly the same as the old one was.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;knife&lt;/code&gt; is Chef’s main client side tool for interacting with all of this. Whilst it’s
designed to be used to interact with the Chef Server, it can also be used locally
to assemble cookbooks. Once you hunt around, you’ll also see that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;knife&lt;/code&gt; has plugins
available to interact with &lt;a href=&quot;http://docs.opscode.com/plugin_knife_ec2.html&quot;&gt;EC2&lt;/a&gt;, &lt;a href=&quot;http://docs.opscode.com/plugin_knife_openstack.html&quot;&gt;OpenStack&lt;/a&gt; and &lt;a href=&quot;http://docs.opscode.com/plugin_knife.html&quot;&gt;many others&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I’m missing a bunch of stuff out here because I’m only interested in using Chef with 
Vagrant. Look at the “Further Reading” heading at the bottom.&lt;/em&gt;&lt;/p&gt;

&lt;h4 id=&quot;building-cookbooks&quot;&gt;Building Cookbooks&lt;/h4&gt;

&lt;p&gt;Cookbooks look a little bit like this (with each indent being a subdirectory):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cookbooks
    cookbook-name
        recipes
            default.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The root &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cookbooks&lt;/code&gt; represents the default collection of cookbooks that Chef —
and especially when used with Vagrant — looks for. Inside here are the cookbooks
themselves. A single cookbook can contain multiple recipes, but the one that is
called by default is funnily enough, called, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default.rb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A cookbook is then used to describe the tool which needs installing, for example,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt;. Our cookbook would be called “git” and our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default.rb&lt;/code&gt; recipe might look 
like this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;git&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will ensure that the package from the local package management system, “git” is
installed.&lt;/p&gt;

&lt;p&gt;Of course, that doesn’t necessarily exist on every system’s package manager. Chef
has a library called “&lt;a href=&quot;http://docs.opscode.com/ohai.html&quot;&gt;Ohai&lt;/a&gt;” that sniffs out the node and reports what it is and 
what it can do. And to interact with Ohai, Chef’s DSL provides methods like 
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;value_for_platform&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You might wish to use Opscode’s ‘&lt;a href=&quot;https://github.com/opscode/chef-repo/&quot;&gt;chef-repo&lt;/a&gt;’, but I’d prefer to build this up
myself, at least for now. It is all about learning it, afterall. The same applies
for using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;knife&lt;/code&gt;, which I mentioned before. This can be used to create a cookbook’s
file structure for you.&lt;/p&gt;

&lt;p&gt;Opscode, and the community maintain a collection of cookbooks that you can use.
A lot of these include tested support for lots of different OS styles and Linux
distributions. So you’ll probably often find you might wish to lean towards these.
Opscode have a &lt;a href=&quot;http://community.opscode.com/cookbooks&quot;&gt;community page for shared Cookbooks&lt;/a&gt;. And an organisation
on GitHub containing all of the &lt;a href=&quot;https://github.com/opscode-cookbooks/&quot;&gt;ones which they manage themselves&lt;/a&gt;.
I’ll use some of these later as submodules.&lt;/p&gt;

&lt;h4 id=&quot;vagrant-integration&quot;&gt;Vagrant Integration&lt;/h4&gt;

&lt;p&gt;Vagrant supports configuring Chef from inside the Vagrantfile. Typically, you’d
define a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node.json&lt;/code&gt; file. This would contain the “run list” — the list of
recipes that a node should manage. Vagrant handles writing, and copying over the VM
this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node.json&lt;/code&gt; file for you on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vagrant up&lt;/code&gt; or on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vagrant reload&lt;/code&gt;. You can also 
assign roles, add recipes or assign a “data bag” that should be used. The 
configuration for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; cookbook above should look something like this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Vagrant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;box&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;precise32&quot;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provision&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:chef_solo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chef&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;chef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_recipe&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;git&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will ensure Chef Solo is invoked when you start up your Vagrant VM, adding “git”
to it’s run list.&lt;/p&gt;

&lt;h4 id=&quot;a-cookbook-for-dotfiles&quot;&gt;A Cookbook for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotfiles&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;That’s all well and good. But, a more complex worked example is more useful. I want
to be able to run a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git clone&lt;/code&gt; on my &lt;a href=&quot;https://github.com/nickcharlton/dotfiles&quot;&gt;dotfiles&lt;/a&gt; and then run the included 
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setup.sh&lt;/code&gt; script, but before I do this, I need to also ensure that all of it’s 
dependencies (and the tools I expect) are installed. On top of this, I want a set of
keys copied over so that I can access the likes of GitHub and so forth.&lt;/p&gt;

&lt;p&gt;To do this, I’ll create a cookbook called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotfiles&lt;/code&gt; which is tasked with installing
the client utilities I expect (git, vim, tmux). Followed by configuring the keys.
After this, it will pul down a clone of my dotfiles and running it’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setup.sh&lt;/code&gt; 
script.&lt;/p&gt;

&lt;p&gt;But before this is installed, I’ll run a few more recipes which install the system 
libraries and build environment. Then we’ll end up with a Vagrantfile which looks 
something like this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Vagrant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;box&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;precise32&quot;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provision&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:chef_solo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chef&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;chef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;dotfiles&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;user&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;vagrant&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;group&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;vagrant&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;public_key&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expand_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;~/.ssh/id_rsa.pub&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;private_key&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;IO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expand_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;~/.ssh/id_rsa&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;chef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_recipe&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;build-essential&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;chef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_recipe&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;python&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;chef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_recipe&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;dotfiles&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The recipes will be installed in the same order as they are listed here. The
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chef.json&lt;/code&gt; call passes along a data bag. This is used to provide the target user
(Chef is run as root, and I probably won’t always want to be using my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotfiles&lt;/code&gt;
cookbook like this) and to pass along my own public/private key pair&lt;sup id=&quot;fnref:keys&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:keys&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, by
using a quick one-liner to suck up the file and pass it along as a string.&lt;/p&gt;

&lt;p&gt;In the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotfiles&lt;/code&gt; cookbook, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default.rb&lt;/code&gt; looks like this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# tools&lt;/span&gt;
&lt;span class=&quot;sx&quot;&gt;%w(git vim vim-scripts tmux)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pkg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pkg&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;home_dir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/home/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;dotfiles&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;user&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# setup ssh keys&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;home_dir&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/.ssh/id_rsa&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;dotfiles&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;user&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;dotfiles&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;group&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;0600&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;dotfiles&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;private_key&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;action&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:create&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;home_dir&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/.ssh/id_rsa.pub&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;owner&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;dotfiles&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;user&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;dotfiles&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;group&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;0600&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;dotfiles&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;public_key&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;action&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:create&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# sync dotfiles&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;home_dir&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/dotfiles&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;git://github.com/nickcharlton/dotfiles.git&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;reference&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;master&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;enable_submodules&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;dotfiles&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;user&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;dotfiles&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;group&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;action&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:checkout&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# setup dotfiles&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;bash&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;setup_dotfiles&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;cwd&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;home_dir&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/dotfiles&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;dotfiles&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;user&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;dotfiles&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;group&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;environment&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;HOME&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;home_dir&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;./setup.sh&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can use a Ruby whitespace array (the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%w()&lt;/code&gt; bit) to iterate around a list of
packages, and pass that to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package&lt;/code&gt; method. This works fine for me, as I’m only
going to use Debian/Ubuntu. Then, we create the ssh key files, by using the values
from the data bag.&lt;/p&gt;

&lt;p&gt;For the last bit, we clone a copy of my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotfiles&lt;/code&gt; repo (including it’s multiple
submodules) and run the setup script. The environment variable of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$HOME&lt;/code&gt; specifies
where the user’s home directory, so we override this so that the script can handle
being run by root.&lt;/p&gt;

&lt;p&gt;The process of setting up a new environment is now a matter of cloning the
“&lt;a href=&quot;https://github.com/nickcharlton/test-environment&quot;&gt;test-environment&lt;/a&gt;” repository, then running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vagrant up&lt;/code&gt;. On my 2009 MacBook Pro,
on a crappy DSL connection (there’s a few packages to grab) this takes just under
5 minutes, but without grabbing the packages, the Chef run only takes 30 seconds of
that. With a good connection (or a local &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-cache&lt;/code&gt;) and something a bit faster
than this machine you could cut that down quite a bit.&lt;/p&gt;

&lt;h3 id=&quot;further-reading&quot;&gt;Further Reading&lt;/h3&gt;

&lt;p&gt;This is just enough Chef to setup a basic Debian/Ubuntu environment for the way I
like things, but this should put you in enough of a position to understand the basic
concepts. So the next step would be to jump over to the main &lt;a href=&quot;http://docs.opscode.com&quot;&gt;Chef documentation&lt;/a&gt;.
It’s quite readable once you understand the basics.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:ruby&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;You don’t really need to know all that much Ruby to get working with 
Vagrant or Chef. But I would recommend at least knowing the basics of the 
syntax. &lt;a href=&quot;#fnref:ruby&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:keys&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;I should really be using a seperate key pair here, but that easy enough
to change. &lt;a href=&quot;#fnref:keys&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</summary>
    </entry>
    
    <entry>
        <title>On the Long Journey to Production</title>
        <link href="https://nickcharlton.net/posts/long-journey-to-production.html" />
        <id>https://nickcharlton.net/posts/long-journey-to-production.html</id>
        <published>Tue, 02 Apr 2013 00:00:00 +0000</published>
        <updated>Tue, 02 Apr 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://rc3.org/2013/03/31/the-long-journey-toward-production/&quot;&gt;Rafe Colburn talks about the extra work that is required to take a simple project and push it into production&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There’s something to be said here as it being the difference between “hacking” and
“software engineering”. Software Engineering is all about the other stuff that comes
once you have a something working. It’s about elegance, design patterns, testing and
the interactions with operations.&lt;/p&gt;

&lt;p&gt;I suppose this is partly why I find DevOps so interesting, it’s merging it all
together, demonstrating to both sides of the coin the effort that is required to
have cost effective, reliable services.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>On the Future of Humanity</title>
        <link href="https://nickcharlton.net/posts/human-extinction.html" />
        <id>https://nickcharlton.net/posts/human-extinction.html</id>
        <published>Tue, 02 Apr 2013 00:00:00 +0000</published>
        <updated>Tue, 02 Apr 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://www.aeonmagazine.com/world-views/ross-andersen-human-extinction/&quot;&gt;Ross Andersen talks about the future of humanity, touching on human extinction&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Stuart Armstrong, a research fellow at the Future of Humanity Institute, once 
illustrated this phenomenon to me with has pithy take on recent primate evolution. 
‘The difference in intelligence between humans and chimpanzees is tiny,’ he said. 
‘But in that difference lies the contrast between 7 billion inhabitants and a 
permanent place on the endangered species list. That tells us it’s possible for a 
relatively small intelligence advantage to quickly compound and become decisive.’&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A slightly fantasised, but interesting look at the future of humanity and AI.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Updates of March</title>
        <link href="https://nickcharlton.net/posts/updates-of-march.html" />
        <id>https://nickcharlton.net/posts/updates-of-march.html</id>
        <published>Sun, 31 Mar 2013 00:00:00 +0000</published>
        <updated>Sun, 31 Mar 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;In which I document some changes I made around here…&lt;/p&gt;

&lt;p&gt;To start with, I’ve adjusted the leading, measure and font size to make it a little
easier to read. This was partly motivated by &lt;a href=&quot;https://speakerdeck.com/danbarber/design-eye-for-the-developer-guy&quot;&gt;Dan Barber’s talk&lt;/a&gt; at &lt;a href=&quot;/posts/digpen-vi.html&quot;&gt;Digpen VI&lt;/a&gt;
I also rebalanced the header, but unless you visited in the last few days, you won’t
notice.&lt;/p&gt;

&lt;p&gt;Secondly, I added a “Link” section. I intend to link to things I come across every 
so often, sometimes with a bit more than a line of commentry. You’ll see more when 
I read longer and more indepth things.&lt;/p&gt;

&lt;p&gt;It’s in the same Atom feed (and on the homepage) but implemented as a seperate
directory. I might do something more with it in future. But you will see it displayed 
differently on the site itself; it’ll always have a “→” and the post metadata 
will say “Linked on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;date&amp;gt;&lt;/code&gt;”, rather than “Posted on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;date&amp;gt;&lt;/code&gt;”.&lt;/p&gt;

&lt;p&gt;This is implemented in the same way as posts, but to provide both (posts and links)
to the index page and atom feed, I’m using a regex, like so:&lt;/p&gt;

&lt;div class=&quot;language-haskell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;setFieldPageList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;take&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;myChronological&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;s&quot;&gt;&quot;templates/post_full.html&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;posts&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regex&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;^(posts|links)/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(where take 3 . myChronological is a sorts differently than the default, the
template string is where it renders to and “posts” is in the infull.)&lt;/p&gt;

&lt;p&gt;I should move to Hakyll 4 and no doubt it’ll be easier than I expect; but I’m 
waiting until I finish the write up part of my degree. Also known as the next few 
weeks.&lt;/p&gt;

&lt;p&gt;And of course the last two posts make up some of the stuff I’ll start writing up
from my project, a retrospect over a month, maybe.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Beer Selection</title>
        <link href="https://nickcharlton.net/posts/beer-selection.html" />
        <id>https://nickcharlton.net/posts/beer-selection.html</id>
        <published>Sat, 30 Mar 2013 00:00:00 +0000</published>
        <updated>Sat, 30 Mar 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://www.drbunsen.org/beer-selection/&quot;&gt;Seth Brown applies statistics and some Python to beer selection&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s a great idea, but also a nice implementation. Something similar applied to 
Whisky would be interesting.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Outputting Matplotlib Plots for the Web</title>
        <link href="https://nickcharlton.net/posts/outputting-matplotlib-plots.html" />
        <id>https://nickcharlton.net/posts/outputting-matplotlib-plots.html</id>
        <published>Thu, 28 Mar 2013 00:00:00 +0000</published>
        <updated>Thu, 28 Mar 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;For the past few days, I’ve been working with Matplotlib and collecting together a 
bunch of notes on animation. It lead to my previous post, 
&lt;a href=&quot;/posts/drawing-animating-shapes-matplotlib.html&quot;&gt;Drawing and Animating Shapes with Matplotlib&lt;/a&gt;. In doing so, I realised 
I’d only embedded plots inside PDFs (when they weren’t part of some sort of 
application) and so, whilst getting bitmap images out of Matplotlib is quite easy,
it’s not so optimal for the web. I’d rather use SVGs.&lt;/p&gt;

&lt;p&gt;But, Matplotlib was designed to produce plots for publications, and so it’s centred 
around printing. And so we have to deal with DPIs, inches and boundary boxes and a
bit of configuration.&lt;/p&gt;

&lt;p&gt;So, given the &lt;a href=&quot;http://scipy-lectures.github.com/intro/matplotlib/matplotlib.html&quot;&gt;basic sine wave plot below&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;numpy&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subplot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;111&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;linspace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;S&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ylim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xlim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;savefig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;sine_wave_plot.svg&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;figure&gt;
    &lt;img src=&quot;/resources/images/sine_wave_plot.svg&quot; width=&quot;500px&quot; alt=&quot;Figure 1: A Simple Sine Wave&quot; /&gt;
    &lt;figcaption&gt;Figure 1: A Simple Sine Wave&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The last line handles saving in the simplest of forms. This gives us a standard
sized SVG file. A nice way to calculate the resulting size is below. This was helped
by &lt;a href=&quot;http://stackoverflow.com/questions/332289/how-do-you-change-the-size-of-figures-drawn-with-matplotlib&quot;&gt;this Stack Overflow question about page sizes&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;dpi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_dpi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_size_inches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;DPI: %i&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dpi&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Size in inches: %i x %i&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Pixels: %i x %i&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dpi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dpi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Typical screen DPI is 72 (with print usually around 300), so that gives us the
scaling factor. But, there’s no reason why this cannot be 100 and using this
gives more obvious result. We can then use the simple equation of: pixels ÷ 
DPI to figure out the inches. So, for a 700 x 650 image, you’d want to specify 7 x 
6.5 inches:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_dpi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_size_inches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;6.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The resulting inches measurement will be rounded to the nearest integer, but this
will still translate to pixels. So, a 7 x 6.5 inch image will report being 7 x 6,
even though in the example above it’ll produce a 700 x 650 image.&lt;/p&gt;

&lt;p&gt;To keep the aspect ratio correct, 50 pixels are added in the above example to take
into account the y axis. Notably, the plot dispayed with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;show()&lt;/code&gt; won’t respect the
same aspect ratio as the saved file.&lt;/p&gt;

&lt;p&gt;The next thing to look at is the border around the plot. The simplest thing to
configure is that of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tight_layout&lt;/code&gt;. If turned on, this reduces the white border
around the outside of the plot. The same will be applied to each subplot. After
enabling this, you get Figure 2.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_tight_layout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;figure&gt;
    &lt;img src=&quot;/resources/images/sine_wave_plot_tight.svg&quot; width=&quot;500px&quot; alt=&quot;Figure 2: Sine Wave Plot with Tight Layout&quot; /&gt;
    &lt;figcaption&gt;Figure 2: Sine Wave Plot with Tight Layout&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;And so, with a little bit of extra effort, it’s quite possible to get perfectly
sized and positioned plots using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;savefig&lt;/code&gt;. Using SVG means that the file size is
relatively small and it can be scaled without losing quality — much nicer on 
Retina displays — and they can be embedded like any other image.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Drawing and Animating Shapes with Matplotlib</title>
        <link href="https://nickcharlton.net/posts/drawing-animating-shapes-matplotlib.html" />
        <id>https://nickcharlton.net/posts/drawing-animating-shapes-matplotlib.html</id>
        <published>Wed, 27 Mar 2013 00:00:00 +0000</published>
        <updated>Wed, 27 Mar 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;As well a being the best Python package for drawing plots, &lt;a href=&quot;http://matplotlib.org/&quot;&gt;Matplotlib&lt;/a&gt; also has 
impressive primitive drawing capablities. In recent weeks, I’ve been using 
Matplotlib to provide the visualisations for a set of &lt;a href=&quot;http://en.wikipedia.org/wiki/Robotic_mapping&quot;&gt;robot localisation&lt;/a&gt; 
projects, where we can use rectangles, circles and lines to demonstrate landmarks, 
robots and paths. Combined with &lt;a href=&quot;http://www.numpy.org/&quot;&gt;NumPy&lt;/a&gt; and &lt;a href=&quot;http://www.scipy.org/&quot;&gt;SciPy&lt;/a&gt;, this provides a quite 
capable simulation environment.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: You should already know how to work with Matplotlib. If you don’t, I suggest
either &lt;a href=&quot;http://www.amazon.co.uk/gp/product/1847197906/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=1847197906&amp;amp;linkCode=as2&amp;amp;tag=nisbl-21&quot;&gt;Matplotlib for Python Developers&lt;/a&gt; or the &lt;a href=&quot;http://scipy-lectures.github.com/&quot;&gt;SciPy Lecture Notes&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Primative shapes in Matplotlib are known as patches, and are provided by the patches
module. Subclasses of patch provide implementations for Rectangles, Arrows, Ellipses
(and then Arcs, Circles) and so on. All of this is part of the &lt;a href=&quot;http://matplotlib.org/api/artist_api.html&quot;&gt;Artist&lt;/a&gt; API, which
also provides support for text. In fact, everything drawn using Matplotlib is part
of the artists module. It’s just a different level of access for drawing shapes
compared to plots.&lt;/p&gt;

&lt;h3 id=&quot;drawing&quot;&gt;Drawing&lt;/h3&gt;

&lt;p&gt;There are multiple ways to write Matplotlib code&lt;sup id=&quot;fnref:ways&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:ways&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Whilst I’m using Pyplot in 
the demonstrations below, the usage is essentially the same. The differences are in 
how the figure is initialised.&lt;/p&gt;

&lt;p&gt;Drawing is a matter of adding the patch to the current figure’s axes, which using 
Pyplot looks something like this:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;axes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;circle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.75&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;y&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gca&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;axis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;scaled&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gca()&lt;/code&gt; returns the current Axis instance. Setting the axis to “scaled” ensures that
you can see the added shape properly. This should give you something like Figure 
2&lt;sup id=&quot;fnref:colours&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:colours&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/mpl_circles_example.svg&quot; width=&quot;500px&quot; alt=&quot;Figure 2: Circles&quot; /&gt;
  &lt;figcaption&gt;Figure 2: Circles&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;rectangles&quot;&gt;Rectangles&lt;/h3&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;rectangle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;r&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gca&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rectangle&lt;/code&gt; is a &lt;a href=&quot;http://matplotlib.org/api/artist_api.html#matplotlib.patches.Rectangle&quot;&gt;Rectangle patch&lt;/a&gt;. It accepts a tuple of the bottom left hand
corner, followed by a width and a height.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kwargs&lt;/code&gt; of either &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ec&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fc&lt;/code&gt; set the edge or face colours respectively. In this
case, it gives us a red rectangle without a border. Various others are also supported,
as this is just a subclass of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Patch&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;circles&quot;&gt;Circles&lt;/h3&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;circle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.75&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;y&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gca&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circle&lt;/code&gt; is a &lt;a href=&quot;http://matplotlib.org/api/artist_api.html#matplotlib.patches.Circle&quot;&gt;Circle patch&lt;/a&gt;. It accepts a tuple of the centre point, and then the
radius.&lt;/p&gt;

&lt;p&gt;The argument of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fc&lt;/code&gt; gives us a yellow circle, without a border.&lt;/p&gt;

&lt;h3 id=&quot;lines&quot;&gt;Lines&lt;/h3&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Line2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lw&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gca&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A basic line is a &lt;a href=&quot;http://matplotlib.org/api/artist_api.html#module-matplotlib.lines&quot;&gt;Line2D&lt;/a&gt; instance. Note that it’s an Artist itself and so its 
not added as a patch. The first tuple gives the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; positions of the line, the
second gives the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;y&lt;/code&gt; positions. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lw&lt;/code&gt; specifies the line width. Much like lines that 
are part of plots in Matplotlib, the line has a lot of configurable styles, such 
as the following:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;dotted_line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Line2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lw&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;5.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                         &lt;span class=&quot;n&quot;&gt;ls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;-.&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;marker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;.&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                         &lt;span class=&quot;n&quot;&gt;markersize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                         &lt;span class=&quot;n&quot;&gt;markerfacecolor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;r&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                         &lt;span class=&quot;n&quot;&gt;markeredgecolor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;r&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                         &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gca&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dotted_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;which gives the lower line in Figure 3. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls&lt;/code&gt; defines the line style and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;marker&lt;/code&gt;
gives the start and end points.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/mpl_two_lines.svg&quot; width=&quot;500px&quot; alt=&quot;Figure 3: Two Lines&quot; /&gt;
  &lt;figcaption&gt;Figure 3: Two Lines&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;em&gt;Note: If you can’t see the lines and only the end markers: There’s a &lt;a href=&quot;http://code.google.com/p/chromium/issues/detail?id=135321&quot;&gt;Bug in WebKit&lt;/a&gt;
which stops you seeing straight lines. You probably can’t see the plot grid lines,
either.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;polygons&quot;&gt;Polygons&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://matplotlib.org/api/artist_api.html#matplotlib.patches.Polygon&quot;&gt;Polygons&lt;/a&gt; are just a series of points connected by lines — allowing you to
draw complex shapes. The Polygon patch expects an Nx2 array of points.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;points&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;polygon&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Polygon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Polygons are also a nice way to implement a multi-step line, this can be done by
tuning the Polygon constructor somewhat:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;points&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Polygon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;closed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edgecolor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;r&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This gives the red line in Figure 4. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;closed&lt;/code&gt; stops Matplotlib drawing a line
between the first and last lines. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fill&lt;/code&gt; is the colour that goes inside the shape,
setting this to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;None&lt;/code&gt; removes it and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;edgecolor&lt;/code&gt; gives the line it’s colour.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/mpl_polygons.svg&quot; width=&quot;500px&quot; alt=&quot;Figure 4: Polygons&quot; /&gt;
  &lt;figcaption&gt;Figure 4: Polygons&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;animation&quot;&gt;Animation&lt;/h3&gt;

&lt;p&gt;The interest here is to move certain shapes around, and in the case of something
like a line (which could, for example, represent a path) update its state. Here,
I’m going to get a ball to orbit around a central point at a set distance away from
it:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;numpy&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;matplotlib&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pyplot&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;matplotlib&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animation&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_dpi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_size_inches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;6.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;ax&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;axes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xlim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ylim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.75&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;y&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;center&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;animate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;center&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;radians&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;radians&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;center&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;anim&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FuncAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                               &lt;span class=&quot;n&quot;&gt;init_func&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                               &lt;span class=&quot;n&quot;&gt;frames&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;360&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                               &lt;span class=&quot;n&quot;&gt;interval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                               &lt;span class=&quot;n&quot;&gt;blit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To do this, I’m just using the equation for a point on a circle (but with the sine/
cosine flipped from the typical — this just means it goes around in 
clockwise), and using the animate function’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt; argument to help compute it. This 
works because I’ve got 360 frames.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init()&lt;/code&gt; function serves to setup the plot for animating, whilst the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;animate&lt;/code&gt;
function returns the new position of the object. Setting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;blit=True&lt;/code&gt; ensures that
only the portions of the image which have changed are updated. This helps hugely
with performance. The purpose of returning &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;patch,&lt;/code&gt; from both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;animate()&lt;/code&gt;
is to tell the animation function which artists are changing. Both of these except
a tuple (as you can be animating multiple different artists.) The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Circle&lt;/code&gt; is
initially created off screen as we need to initialise it before animating. Without
initialising off screen, blitting causes a bit of an artifact.&lt;/p&gt;

&lt;p&gt;And so, this (with the addition of the section below) produces the video in Figure 5
below:&lt;/p&gt;

&lt;figure&gt;
  &lt;video src=&quot;/resources/images/mpl_ball_animation.mp4&quot; type=&quot;video/mp4&quot; width=&quot;500px&quot; poster=&quot;/resources/images/mpl_ball_animation_poster.png&quot; controls=&quot;&quot;&gt;
    Dammit. You can&apos;t see this video. It&apos;s MP4 (H.264).
    But, you can &lt;a href=&quot;&quot;&gt;download it and see it that way&lt;/a&gt;.
  &lt;/video&gt;
  &lt;figcaption&gt;Figure 5: The Ball Animation as a Video&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;a href=&quot;http://jakevdp.github.com/blog/2012/08/18/matplotlib-animation-tutorial/&quot;&gt;Jake Vanderplas’ notes on Matplotlib&lt;/a&gt; were invaluable in figuring
this section out. Notably in blitting&lt;sup id=&quot;fnref:blitting&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:blitting&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. But generally, simple Artist
animation is a bit thin on the ground. Hopefully this helps with that somewhat.&lt;/p&gt;

&lt;h3 id=&quot;output&quot;&gt;Output&lt;/h3&gt;

&lt;p&gt;I initially wanted to be able to export in two formats, one as an H.264 video, like 
the one above and as an animated gif. My initial assumption was that a gif would
most likely have less of an overhead than that of a video and it would avoid
browser inconsistencies.&lt;/p&gt;

&lt;p&gt;To solve the video export, Matplotlib comes with support for exporting video 
sequences from an animation using the save method on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Animate&lt;/code&gt;. It pipes out support
for video to &lt;a href=&quot;http://www.ffmpeg.org/&quot;&gt;ffmpeg&lt;/a&gt;, but video formats are somewhat fickle and so you need to add
a few more flags to get it to render correctly.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;anim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;animation.mp4&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fps&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
          &lt;span class=&quot;n&quot;&gt;extra_args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;-vcodec&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;h264&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                      &lt;span class=&quot;s&quot;&gt;&apos;-pix_fmt&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;yuv420p&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This saves an H.264 file to ‘animation.mp4’ in a format supported by QuickTime&lt;sup id=&quot;fnref:pfmt&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:pfmt&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;. 
In &lt;a href=&quot;http://jakevdp.github.com/blog/2012/08/18/matplotlib-animation-tutorial/&quot;&gt;Jake Vanderplas’&lt;/a&gt; animation tutorial he doesn’t specify a
pixel format and it works fine. I suspect this might be something to do with ffmpeg
defaults. You’ll also need to be using Matplotlib version 1.2.0 or greater (I had 
issues with 1.1.0.)&lt;/p&gt;

&lt;p&gt;Sadly, Matplotlib doesn’t support producing gifs on it’s own, but this was only the
first of a few issues. We can convert the image we’ve just produced using ffmpeg 
and stitch it back together as a gif using &lt;a href=&quot;http://www.imagemagick.org/script/index.php&quot;&gt;ImageMagick&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ffmpeg &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; animation.mp4 &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; 10 output%05d.png

convert output&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.png output.gif
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, ffmpeg converts the video file from before into a series of PNG files, then
we use ImageMagick’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;convert&lt;/code&gt; command to push these back together into a gif. On 
its own, this ended up with a 4MB file. Even the video was only 83KB and so this
isn’t so great. After attempting to compress the final output gif using ImageMagick
(they have a huge article on &lt;a href=&quot;http://www.imagemagick.org/Usage/anim_opt/&quot;&gt;Animation Optimisation&lt;/a&gt;), I turned to compressing 
each input file using &lt;a href=&quot;http://imageoptim.com&quot;&gt;ImageOptim&lt;/a&gt;. This ended up giving me a final image size of
8.0MB (and took a good hour.) Worse than I started with.&lt;/p&gt;

&lt;p&gt;With this, I concluded that a gif isn’t that great of an option. I’d like to see 
animated SVG support for Matplotlib, but I’d wonder if this required moving to 
something which was stylised using a Document-Object-Model (DOM). Michael Droettboom
touched on this in his &lt;a href=&quot;http://mdboom.github.com/blog/2013/03/25/matplotlib-lessons-learned/&quot;&gt;“Matplotlib Lessons Learned” post&lt;/a&gt;. Even
for something much more complex than these contrived examples, using JavaScript to
animate the SVG is much better option. But for now, a video is the best way.&lt;/p&gt;

&lt;p&gt;And that’s about it. One of the advantages of having the Matplotlib provided grid
is that the numbers are relatively easy to determine — for my purposes this
means hooking the view to the calculations.&lt;/p&gt;

&lt;p&gt;Pylab provides a merging of the Numpy and Matplotlib namespaces to ease transition
   from MATLAB. It’s not recommended and anyway I’m a programmer — not MATLAB 
   user — first.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;And then I&apos;d use the OOP method for embedding in bigger projects, I&apos;ve done
this for use in PyQt, for example. The OOP method is slightly more complex, but
a better alternative if you need to display multiple, similar plots using 
different data.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:ways&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;I typically use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyplot&lt;/code&gt;, rather than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pylab&lt;/code&gt; to keep the namespace clean. &lt;a href=&quot;#fnref:ways&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:colours&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Your colours will probably vary. I use Huy Nguyen’s 
        “&lt;a href=&quot;http://www.huyng.com/posts/sane-color-scheme-for-matplotlib/&quot;&gt;Sane colour scheme&lt;/a&gt;”. &lt;a href=&quot;#fnref:colours&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:blitting&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;The return value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;animate()&lt;/code&gt; must contain the items which have
changed, but also those inside the figure,  otherwise you’ll see a 
mess of artifacts working their away across the image. &lt;a href=&quot;#fnref:blitting&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:pfmt&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;To be more specific, it seems that QuickTime doesn’t support the Planar 
4:4:4 YUV pixel format which ffmpeg outputs by default. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-pix-fmt&lt;/code&gt; flag
specifies using Planar 4:2:0 YUV which it does support.&lt;/p&gt;

      &lt;p&gt;However, Jake’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;save&lt;/code&gt; doesn’t have this flag, but it is encoded in 4:2:0. Why?
I have no idea. &lt;a href=&quot;#fnref:pfmt&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</summary>
    </entry>
    
    <entry>
        <title>Digpen VI</title>
        <link href="https://nickcharlton.net/posts/digpen-vi.html" />
        <id>https://nickcharlton.net/posts/digpen-vi.html</id>
        <published>Sun, 24 Mar 2013 00:00:00 +0000</published>
        <updated>Sun, 24 Mar 2013 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Yesterday was Digpen VI, down at the Eden Project in Cornwall. It was a wonderful
day spanning a breadth of different topics, and at a nice location.&lt;/p&gt;

&lt;p&gt;It’s quite fantastic to see how far Digpen has come from where it started. Way back
when &lt;a href=&quot;/posts/the-digital-peninsulas-first-web-unconference.html&quot;&gt;in 2011, we had 80 people in a classroom at Plymouth University&lt;/a&gt;, to
now having a full day of talks, split across two tracks, covering a wide selection
of web topics and all at a lovely venue. &lt;a href=&quot;http://www.sophiedennis.co.uk/&quot;&gt;Sophie&lt;/a&gt; put much of it down to the
community itself — this is certainly true, it’s a diverse group of people far
more happy to put more into it than they take out — but without her (and Andy’s
steering) behind it, it wouldn’t be what it is today.&lt;/p&gt;

&lt;p&gt;The talks started off with &lt;a href=&quot;https://twitter.com/matconnolley&quot;&gt;Matt Connelly&lt;/a&gt; talking about using some data analysis
techniques to trace where &lt;a href=&quot;http://www.iteracy.com/&quot;&gt;Iteracy&lt;/a&gt;’s clients have come from. This was rather fascinating.
The root message here was to suggest that whilst certain smaller clients may not
seem so worthwhile at the time, without looking at the data you don’t know where
this project will lead — it could well be a much larger project.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://thisisthechris.co.uk/&quot;&gt;Chris&lt;/a&gt; then went on to talk about the &lt;a href=&quot;http://www.mobydickbigread.com/&quot;&gt;Moby Dick Big Read&lt;/a&gt;, and the 
technical (and human) challenges that occur when a project relies on a large 
amount of people (none of whom technical) and suddenly becomes rather 
popular&lt;sup id=&quot;fnref:server&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:server&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. This was followed by a break, after which we split into two tracks.&lt;/p&gt;

&lt;p&gt;I’m not usually much of a fan of conferences with different tracks. &lt;a href=&quot;/posts/brussels-fosdem-2012.html&quot;&gt;FOSDEM&lt;/a&gt; or
&lt;a href=&quot;/posts/orgcon-2012.html&quot;&gt;ORGCon&lt;/a&gt; for example, always felt like you were missing out on something. But
this was done well. In fact, the track I followed hah a bit of a &lt;a href=&quot;/posts/dconstruct-2012.html&quot;&gt;dConstruct&lt;/a&gt; vibe 
to it. But, the other track quite successfully seemed to fill in the more technical
side (from those I talked to after.)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://twitter.com/willskates&quot;&gt;Will&lt;/a&gt; talked about making time for projects outside of the day job, of planning
out long term ones and making it all fit in. He emphasised planning out as much as
possible, splitting big jobs into managable ones and allowing bits of projects to
slot in where they can. He also referenced &lt;a href=&quot;http://www.merlinmann.com/&quot;&gt;Merlin Mann&lt;/a&gt;’s ideas of deliberately
planning and making time for everything. Of keeping the devices tucked away when
it’s most appropriate and planning blocks of time to keep us present at all of the
things that matter.&lt;/p&gt;

&lt;p&gt;Following this, &lt;a href=&quot;http://bouncingdan.co.uk/&quot;&gt;Dan Goodwin&lt;/a&gt; talked about not reading and not following 
everything. Because it’s impossible, and so we shouldn’t feel bad about it. As long,
of course, as we are doing something to keep current. After this was lunch, and a
chance to explore the Eden Project. Even in a low-season like it is at the moment,
it’s a fanastic place to explore.&lt;/p&gt;

&lt;p&gt;After lunch, &lt;a href=&quot;https://twitter.com/beardygeek&quot;&gt;Stuart Marsh&lt;/a&gt; talked about how, and the motivations behind building
his service, &lt;a href=&quot;http://issuepop.com/&quot;&gt;Issue Pop&lt;/a&gt;. Much like Matt’s talk at the start of the day, it’s great
to see people sharing this side of projects and their businesses. Whilst we’re
happy to write about or stand up and talk about the design or technical sides of
our businesses, it’s still a little taboo to talk about the actual business side.
After this, &lt;a href=&quot;http://danbarber.me/&quot;&gt;Dan&lt;/a&gt; gave a nice and accessible introduction to Design for Developers.
If you’re in the same boat, I suggest picking up a copy of &lt;a href=&quot;http://www.amazon.co.uk/gp/product/1119998956/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=1119998956&amp;amp;linkCode=as2&amp;amp;tag=nisbl-21&quot;&gt;Design for Hackers&lt;/a&gt;,
it’ll cover much of what he spoke about and the bits he didn’t quite have time to
fit in, too.&lt;/p&gt;

&lt;p&gt;To close off the end of the day was &lt;a href=&quot;http://littlewhalestudio.com/&quot;&gt;Jo and Stephen&lt;/a&gt; talking about maintaining
our creativity. They mostly focused around design and illustration, but everything
transfers well to programming too&lt;sup id=&quot;fnref:creativity&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:creativity&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. Their tips and stories centred around
getting away from everything, and working on other things away from work —
especially in other mediums. My main take away from this, was a reminder that if it
isn’t fun, what’s the point? And that we can, with a bit of planning, keep our
creativity, even at the hardest of times.&lt;/p&gt;

&lt;p&gt;The whole day was wonderful, especially for catching up with all of the friends I’ve 
made over the years over Devon and Cornwall. The last few weeks have been damn hard,
so it also worked as a perfectly timed burnout antidote.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:server&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;And yes, it was me who moved it over to the different server. The host 
       hadn’t done anything to transfer it properly. But, fortunately, 
       Wordpress (and Apache, MySQL, etc) is easy and simple enough to configure. &lt;a href=&quot;#fnref:server&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:creativity&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Programming is as creative as anything else is. It just has a lot of
           other constraints, be it design patterns, architectures or technical
           limitations. Programming is nothing like building widgets. Even if
           you’ve built a very similar product before. &lt;a href=&quot;#fnref:creativity&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</summary>
    </entry>
    
    <entry>
        <title>Blog Updates</title>
        <link href="https://nickcharlton.net/posts/blog-updates.html" />
        <id>https://nickcharlton.net/posts/blog-updates.html</id>
        <published>Wed, 17 Oct 2012 00:00:00 +0000</published>
        <updated>Wed, 17 Oct 2012 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;For a project several weeks in the making, I’ve just pushed up the changes to
this, my site. I started working on all of this way back when I was still on
placement, but then also through Young Rewired State and intermittently
since. It’s been at once the bane of my todo list, and a joy to work on,
experimenting, refactoring and of course, writing.&lt;/p&gt;

&lt;p&gt;It’s in no-way perfect, but it’s a significant improvement from my orignal
version.&lt;/p&gt;

&lt;p&gt;Like most of my projects, it’s also up on
&lt;a href=&quot;https://github.com/nickcharlton/nickcharlton.net&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;posts&quot;&gt;Posts&lt;/h2&gt;

&lt;p&gt;There’s a few posts which I’ve finally been able to post. Most of these had
been sat around as drafts, others needed a bit of completion. But, without
further ado, my last few months:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/posts/nsconf-mini.html&quot;&gt;NSConf Mini: Developers vs. Designers&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/posts/dconstruct-2012.html&quot;&gt;dConstruct 2012&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/posts/young-rewired-state-2012.html&quot;&gt;Young Rewired State 2012&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/posts/finishing-at-rokk-media.html&quot;&gt;Finishing at Rokk Media&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m now a good few weeks into the final-year of my degree and so my focus has
(necessarily) shifted quite a bit. My focus from now on will be mostly centred
around &lt;a href=&quot;http://python.org/&quot;&gt;Python&lt;/a&gt;, &lt;a href=&quot;http://qt.digia.com&quot;&gt;Qt&lt;/a&gt; 
(using &lt;a href=&quot;http://www.riverbankcomputing.co.uk/software/pyqt/intro&quot;&gt;PyQt&lt;/a&gt; and 
some OpenGL), Robotics, 
&lt;a href=&quot;http://www.nvidia.com/object/cuda_home_new.html&quot;&gt;GPU Computation using CUDA&lt;/a&gt; and
various AI topics. I already have a few drafts relating to these in the works
(I find it a great way to learn). I will also be occasionally posting about my
degree project — a Quadrotor platform, Simulator and associated mapping
algorithms. It’s going to be an interesting year.&lt;/p&gt;

&lt;h2 id=&quot;a-colophon&quot;&gt;A Colophon&lt;/h2&gt;

&lt;p&gt;This version is based upon &lt;a href=&quot;http://jaspervdj.be/hakyll/index.html&quot;&gt;Hakyll&lt;/a&gt;. 
It’s a static site generator written in Haskell. Before this, I tried out multiple 
others, from &lt;a href=&quot;http://hyde.github.com/&quot;&gt;Hyde&lt;/a&gt; to
&lt;a href=&quot;http://mynt.mirroredwhite.com/&quot;&gt;Mynt&lt;/a&gt;, and the original, 
&lt;a href=&quot;https://github.com/mojombo/jekyll&quot;&gt;Jekyll&lt;/a&gt;. But each had issues in one sense or 
another and didn’t work all that well for me. Obviously, your requirements will 
vary.&lt;/p&gt;

&lt;p&gt;But, the key part of Hakyll is it’s use of
&lt;a href=&quot;http://johnmacfarlane.net/pandoc/&quot;&gt;pandoc&lt;/a&gt;. This is a document
conversion tool (also written in Haskell) that I’ve since started using for
generating documents for print. It also had the
&lt;a href=&quot;http://daringfireball.net/projects/markdown/&quot;&gt;Markdown&lt;/a&gt; extensions that
I wanted (footnotes, citations, LaTeX maths support, tables, etc.)&lt;/p&gt;

&lt;p&gt;The design itself is responsive&lt;sup id=&quot;fnref:responsive&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:responsive&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; — albeit, not tested absolutely
everywhere — but not mobile first (because I’d written it desktop first),
I’ll probably fix this at some point.&lt;/p&gt;

&lt;p&gt;It uses TypeKit to provide “Proxima Nova” for headings, and “Adelle” for the
body text. The icons are font-icons from &lt;a href=&quot;http://pictos.cc/server&quot;&gt;Pictos Server&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Syntax highlighting is provided in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;pre&amp;gt;&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;code&amp;gt;&lt;/code&gt; blocks using
&lt;a href=&quot;http://pygments.org/&quot;&gt;Pygments&lt;/a&gt;.
Mathematics symbols are through &lt;a href=&quot;http://www.mathjax.org/&quot;&gt;MathJax&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s deployed by pushing a Git repository to a VPS hosted with
&lt;a href=&quot;http://prgmr.com/&quot;&gt;Prgmr.com&lt;/a&gt;, which I’ve used for several years now.&lt;/p&gt;

&lt;p&gt;for side projects like this.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:responsive&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;People who aren’t building sites this way should be shot, even &lt;a href=&quot;#fnref:responsive&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</summary>
    </entry>
    
    <entry>
        <title>NSConf Mini: Developers vs. Designers</title>
        <link href="https://nickcharlton.net/posts/nsconf-mini.html" />
        <id>https://nickcharlton.net/posts/nsconf-mini.html</id>
        <published>Thu, 20 Sep 2012 00:00:00 +0000</published>
        <updated>Thu, 20 Sep 2012 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;On Monday, it was NSConf Mini: “Developers vs. Designers”. This was a 1-day
event held up in Leicester by Scotty and the rest of 
&lt;a href=&quot;http://ideveloper.tv&quot;&gt;iDeveloper TV&lt;/a&gt;. The premise of the event was to get 
developers (both Mac and iOS) thinking about design and how to improve their 
products, or the services that they provide to their clients.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://johnnye.net&quot;&gt;John&lt;/a&gt; and I went up horrifically early in the morning and got in just after
Matt Gemmell’s talk started.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Matt Gemmell&lt;/strong&gt; talked about crafting applications. Focusing on experiences
over features, locking to the core idea and apply this to people’s needs and
using people’s emotions to help improve the experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jaime Newbury&lt;/strong&gt; talked about problem solving in projects and what to do
(including how to avoid) problems that (always) occur during projects. The core
message was communication. Typically, either the client or the service provider
fails to communicate properly on one side, causing issues to either occur quite
quickly, or slowly build up over time causing misery for everyone involved.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Communication -&amp;gt; Understanding -&amp;gt; Confidence -&amp;gt; Trust&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But also, to have standards. Firing client is okay, and it’s what will
differentiate you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cathy Shire&lt;/strong&gt; talked about both the frameworks that we use and the
interactions between designers and developers on iOS and Mac projects.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I have a really hard time seperating the design of the product with the
application. To me, there the same thing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;She advocated putting designers and developers at the same cluster of tables
(as had been done at Sofa). There’s always design work, so designers won’t get
bored. At Sofa, tables were organised per project, this involved a designer and
developer in close proximity. If the developer can access the designer (and
vice-versa), queries and assumptions can easily be talked through, saving
issues later on — and avoiding the divide.&lt;/p&gt;

&lt;p&gt;On the side of code: Go for adaptability and resiliance, not reuse. The
frameworks (say, Cocoa, et. al.) provide the reuse for us. Because of this, we
should build frameworks for specific jobs (as Cocoa does for seperable parts.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dave Wiskus&lt;/strong&gt; gave us a story (a rather entertaining one, actually, fitting
for just after lunch.) He stated that “Design is what happens when you create 
and care at the same time”.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Josh Clark&lt;/strong&gt; talked about buttons. They’re a hack, they work, but we can
often do better. Big screens want for big gestures. Whilst your application can
be featureful, your interface shouldn’t be. UI is a social convention, and it
varies from place to place.&lt;/p&gt;

&lt;p&gt;If the content can be navigable, let it be so. Abstractions need to be learned,
so if you can borrow from other areas and keep it obvious, you are improving
the experience for the user. But, if you’re going to take the effort to
implement an stylistic interface, you really should be taking into account all
of the other aspects of it.&lt;/p&gt;

&lt;p&gt;Importantly, remember what it was like when you were getting where you are
today. What was it like when you started? Hard? Well, help make it easier for
everyone.&lt;/p&gt;

&lt;p&gt;After this, most of us went for a curry, followed by a few pub visits. It was
great spending a bunch of time talking to different iOS and Mac developers
— something you don’t get in Devon very often.&lt;/p&gt;

&lt;p&gt;In the process of this I ended up finally talking to 
&lt;a href=&quot;http://babilim.co.uk/&quot;&gt;Alasdair Allan&lt;/a&gt;, briefly catching up with 
&lt;a href=&quot;http://www.goosoftware.co.uk/&quot;&gt;Simon Whitaker&lt;/a&gt;, 
and also meeting; &lt;a href=&quot;http://twitter.com/duncanlowrie&quot;&gt;Duncan Lowrie&lt;/a&gt;, 
&lt;a href=&quot;http://torbjornlunde.com/&quot;&gt;Torbjørn Vik Lunde&lt;/a&gt;, a few people from 
&lt;a href=&quot;http://blackpixel.com/&quot;&gt;Black Pixel&lt;/a&gt; and several others. So, all in all, it was 
a rather good event.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>dConstruct 2012</title>
        <link href="https://nickcharlton.net/posts/dconstruct-2012.html" />
        <id>https://nickcharlton.net/posts/dconstruct-2012.html</id>
        <published>Sat, 08 Sep 2012 00:00:00 +0000</published>
        <updated>Sat, 08 Sep 2012 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Yesterday was &lt;a href=&quot;http://2012.dconstruct.org/&quot;&gt;dConstruct&lt;/a&gt;. I went to dConstruct 
last year, too and it was excellent. But this year, it was quite fantastic. It had 
the right mix of “yes, this!”, “huh, yeah, I hadn’t thought of that” and “wow”. 
&lt;a href=&quot;http://adactio.com/&quot;&gt;Jeremy&lt;/a&gt; and the rest of the people at 
&lt;a href=&quot;http://clearleft.com/&quot;&gt;Clearleft&lt;/a&gt; did a fantastic job. Of course, with people 
from Ben Hammersley to James Burke, it was going to be good.&lt;/p&gt;

&lt;p&gt;After picking up my conference pass on Thursday, I met up with 
&lt;a href=&quot;http://pauladamdavis.com/&quot;&gt;Paul Adam Davis&lt;/a&gt; for burgers. From there I caught 
&lt;a href=&quot;http://preppeller.tumblr.com&quot;&gt;Andrew&lt;/a&gt; and &lt;a href=&quot;http://www.thatcanadiangirl.co.uk&quot;&gt;Vero&lt;/a&gt; 
of &lt;a href=&quot;http://alfredapp.com/&quot;&gt;Alfred&lt;/a&gt;&lt;sup id=&quot;fnref:alfred&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:alfred&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; along with 
&lt;a href=&quot;http://blog.fatbusinessman.com&quot;&gt;David Thompson&lt;/a&gt;. 
We then had some (rather nice) cocktails before heading off to the pre-party,
where I ended up talking with people from &lt;a href=&quot;http://aralbalkan.com&quot;&gt;Aral Balkan&lt;/a&gt; 
to &lt;a href=&quot;http://tantek.com&quot;&gt;Tantek Çelik&lt;/a&gt; and many more nice people too.&lt;/p&gt;

&lt;h2 id=&quot;talks&quot;&gt;Talks&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Ben Hammersley&lt;/strong&gt; talked about beauty being everywhere, but something that we
need to fight for. He cited the web as a good example of this, from what was
once a horrible mess of marquee tags has propped up both a social and cultural
evolution — art pieces to well organised centres of knowledge. But, also
important: We’re the first generation to see exponential growth, and as
a species, we don’t quite know how to deal with it. But, we’ll work it out over
time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jenn Lukas&lt;/strong&gt; talked about learning. There’s tons of stuff that we’d like to
learn. But, more specifically, people from all different corners of the world,
with various different backgrounds are interested in learning to code. Putting
together a shared curriculum gives us a far better way of visualising progress,
and hell, excitement. More importantly, though, just share what you know.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scott Jensen&lt;/strong&gt; talked about “default thinking” — where we use a new product
or service in exactly the same way as we used the last new product or service
until we stumble across the best way to use it.&lt;/p&gt;

&lt;p&gt;And then, he announced that he’d: “work[ed] on the Newton”. There was a huge round
of applaus.&lt;/p&gt;

&lt;p&gt;His core direction was, however pointed towards “apps”. This is something that
fits quite closely to my own thoughts &amp;amp;mash; “Apps aren’t needed for everything
— we need a better interface model.” Currently, we’ve fallen onto the
wrong side of a “Value : Pain” ratio. There are far too many individual (and
pointless) applications available to us that it causes us some form of pain
(be it findability, terrible user experiences, etc.)&lt;/p&gt;

&lt;p&gt;How about we flip the app idea into the devices themselves? Instead the
application will react with it’s environment, rather than the other way
around[^iossix]. This fits well into the Spimes concept[^spimes]. Instead of
what we currently have, we can move towards a “just in time interaction” model.
We’ll see stuff available when we want to interact with it, but it won’t pester
us in general. So far, we’ve succeeded at location-based reminders. We can do
much better.&lt;/p&gt;

&lt;p&gt;The mobile web is a fantastic thing, but we need to cut down the pain of
accessing resources and processing the information around us. And so, a model
of more internet and less web would be quite fantastic (and so, more
distributed, none of this single web property everyone uses.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ariel Waldman&lt;/strong&gt; talked about her work with NASA, design and pushing of
projects such as Space Hack and Science Hack Day.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“When I look at blackholes, I just see massive hackspaces” — Ariel
Waldman.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Lauren Beukes&lt;/strong&gt; talked about the role fiction and storytelling has in
connecting to other people and understanding another persons’ perspective and
how impressively (and indeed, scarily) real dystopian science fiction can
become reality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jason Scott&lt;/strong&gt; talked about his work with the &lt;a href=&quot;http://archive.org&quot;&gt;Internet Archive&lt;/a&gt;
and his opinions on the handling of users’ data. He stated that it’s a crime
that we treat people’s data with such little respect and that we shouldn’t
(paraphrased) “just delete things because you’re done with it — someone
probably wants it.”.&lt;/p&gt;

&lt;p&gt;We should store things — there’s always a sideways value to things.&lt;/p&gt;

&lt;p&gt;And, if you’re running &lt;em&gt;anything&lt;/em&gt; you have both a trust and a responsibility
towards that data. You should certainly provide export capabilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tom Armitage&lt;/strong&gt; talked about toys. Toys make a good caracature of real-life
things — and they can be anything. But they also work as a definition of
a craft.&lt;/p&gt;

&lt;p&gt;Utility and purpose is not the same thing. Something can be useful without
serving a purpose. Or the use might evolve from play. Joan Erikson said: “The
opposite of play is obey, not work”. You can indeed play for work.”&lt;/p&gt;

&lt;p&gt;Making is analogous to playing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;James Burke&lt;/strong&gt; talked about how the past can help us work with the future.
Because we have nowhere else to look.&lt;/p&gt;

&lt;p&gt;Everything is, and will continue to be multi-disciplinary and because of this,
our education system need to adapt. But similarly, everything is an
interconnected web — both concepts and people.&lt;/p&gt;

&lt;p&gt;The growth of nano-technology will undermine all of our current theories and
philosophies as it’s all based upon scarcity. What happens when something that
suddenly breaks can either fix itself, or we can print our own part inside our
home?&lt;/p&gt;

&lt;p&gt;Humanities’ typical outlook of “business and usual” is analogous to falling off
a sky-scraper — it’s going fine until it isn’t, then you realise you’re
screwed.&lt;/p&gt;

&lt;p&gt;A clear message of the whole event was in moving away from multiple web silos
and instead more to a model much more internet like. Collaborating together,
distributed around and without the risks we currently have with both our data
and our futures with the way we’re currently using the web.&lt;/p&gt;

&lt;p&gt;me hovering around them for the few days. Also, 
&lt;a href=&quot;http://www.alfredapp.com/purchase/&quot;&gt;buy Alfred&lt;/a&gt;. 
[^iossix]: Of course, we’re seeing this somewhat with Passbook. But, really, we
want even more environmental interaction, especially if we continue to add
publicly available sensors.
[^spimes]: If you’re interested in all of this Sci-Fi stuff from an
implementable angle, Scott wrote:
&lt;a href=&quot;http://designmind.frogdesign.com/blog/of-bears-bats-and-bees-making-sense-of-the-internet-of-things.html&quot;&gt;Of Bears, Bats, and Bees: Making Sense of the Internet of Things&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:alfred&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;They really are quite a lovely. I don’t quite know how they handled &lt;a href=&quot;#fnref:alfred&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</summary>
    </entry>
    
    <entry>
        <title>Young Rewired State 2012</title>
        <link href="https://nickcharlton.net/posts/young-rewired-state-2012.html" />
        <id>https://nickcharlton.net/posts/young-rewired-state-2012.html</id>
        <published>Mon, 13 Aug 2012 00:00:00 +0000</published>
        <updated>Mon, 13 Aug 2012 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Last week, I had the privilege of helping mentor seven young people as part of
&lt;a href=&quot;http://youngrewiredstate.org/&quot;&gt;Young Rewired State&lt;/a&gt; in Plymouth, and then at 
the weekend up in Birmingham for the Festival of Code.
It was absolutely fantastic.&lt;/p&gt;

&lt;p&gt;It was hosted under the wing of
&lt;a href=&quot;http://www.i-dat.org&quot;&gt;i-DAT, the Plymouth University Institute for Art and Technology&lt;/a&gt; 
who provided rooms and other resources. We started out in the
Dome (officially the Immersive Vision Theatre), with an introduction by iDAT’s
Director, &lt;a href=&quot;http://www.i-dat.org/mike-phillips/&quot;&gt;Mike Phillips&lt;/a&gt;. 
After talking through what iDAT did, &lt;a href=&quot;http://thisisthechris.co.uk&quot;&gt;Chris&lt;/a&gt; introduced 
what the week would be about and then we moved on to what would be are base for the 
rest of the week, one of the labs in Babbage.&lt;/p&gt;

&lt;p&gt;For mentors, we had a pretty nice mix. &lt;a href=&quot;http://phalt.tumblr.com&quot;&gt;Paul&lt;/a&gt; had come 
along with his brother, &lt;a href=&quot;http://projects.drogon.net&quot;&gt;Gordon&lt;/a&gt; (who’s now somewhat 
internet-famous for his lower level work with the Raspberry Pi), and also the 
drop ins of two lecturers, 
&lt;a href=&quot;http://www.plymouth.ac.uk/pages/dynamic.asp?page=staffdetails&amp;amp;id=s1atkinson&quot;&gt;Shirley Atkinson&lt;/a&gt;, 
one of Paul and I’s and then &lt;a href=&quot;http://www.i-dat.org/chris-saunders/&quot;&gt;Chris Saunders&lt;/a&gt; 
from iDAT. And of course, Chris and myself.&lt;/p&gt;

&lt;p&gt;We opened with a short brainstorm of resources we could work with. The goal of
Rewired State is to do smart things with data and this was no different. I also
made a suggestion of talking about some of the projects that we (the mentors) had
worked on to try and give some potential inspiration for what could be done.&lt;/p&gt;

&lt;p&gt;For the rest of the day, they split up into teams and started to
formulate project ideas. By the end of day one, we had four distinct projects
on the go.&lt;/p&gt;

&lt;p&gt;Over the next few days, projects gradually developed; going from mere figments
of imagination on day one, to most of a working product by the Thursday.&lt;/p&gt;

&lt;p&gt;Our projects were:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://twit.tone.brkbrkbrk.com&quot;&gt;TwitTone&lt;/a&gt; by &lt;a href=&quot;http://brkbrkbrk.com/&quot;&gt;Sam Wray&lt;/a&gt; and Simon Barnes.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://yrsidat.wordpress.com/2012/08/08/satellite-tracker/&quot;&gt;Satellite Tracker&lt;/a&gt; by Owen Hallet and Ryan Hockett&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/KingDingDan/space&quot;&gt;ODPlayground&lt;/a&gt; by &lt;a href=&quot;http://www.danielhart.in&quot;&gt;Daniel Hart&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/jynxsp0ck&quot;&gt;Michael Maddocks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;figure&gt;
  &lt;img src=&quot;/resources/images/yrs2012-odplayground.png&quot; /&gt;
  &lt;figcaption&gt;ODPlayground in Action.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;And on the Friday, we headed up to Birmingham for the Festival of Code.&lt;/p&gt;

&lt;p&gt;The Festival of Code was hosted at Birmingham’s 
&lt;a href=&quot;http://www.custardfactory.co.uk&quot;&gt;Custard Factory&lt;/a&gt;, a rather neat place&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; which dubs
itself as a “revolutionary new arts and media quarter”. It’s filled with small
units, as well as a nightclub, bar, café and other larger areas that were
needed to host the Festival of Code. We arrived, registered and positioned
ourselves in one of the lower room, as the teams worked on finishing off their
projects ready to demonstrate the next day.&lt;/p&gt;

&lt;p&gt;Whilst that was underway, Chris and I grabbed some lunch. We also met up with
&lt;a href=&quot;http://neilcford.co.uk&quot;&gt;Neil Ford&lt;/a&gt;, &lt;a href=&quot;http://blog.amyl.org.uk&quot;&gt;Adam McGreggor&lt;/a&gt; and, 
briefly, &lt;a href=&quot;http://andypiper.co.uk&quot;&gt;Andy Piper&lt;/a&gt;, who are always nice to see. After this, 
we explored the venue, scoping out where everything would be.&lt;/p&gt;

&lt;p&gt;After what must have been the biggest delivery of pizza I’d ever seen, then
a set of talks by various different people, a dodgy night’s sleep on the Old 
Theatre’s floor was had. Saturday was judging day. As a group, we filed into 
Theatre 2 and then sat and watched a fantastic set of demos of projects in our 
category. This went on for the rest of the day (there were many projects), up
until the main event in the evening.&lt;/p&gt;

&lt;p&gt;The main event was huge. It bought everyone together to see the projects who
had made it in to the final stages. One of our projects, TwitTone also made it 
into the final![^2] Then was the judging, followed by prize giving.&lt;/p&gt;

&lt;p&gt;Seeing some of the many projects that had made it into the final was
incredible. Even after going to hack days myself, I will be continually amazed
by what people can do in a short period of time (very well polished stuff, too)
on something that catches their eye. It’s even better when it’s young people.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://mulqueeny.wordpress.com&quot;&gt;Emma&lt;/a&gt;, the rest of the people from Rewired State 
and the many, many volunteers did a fantastic job, making it a very good week for
everyone involved.&lt;/p&gt;

&lt;p&gt;Want to read more? There’s a 
&lt;a href=&quot;http://www.flickr.com/photos/nickcharlton/sets/72157631761238662/&quot;&gt;set of photos I took on Flickr&lt;/a&gt;
and the &lt;a href=&quot;http://yrsidat.wordpress.com&quot;&gt;YRS iDAT 2012 site&lt;/a&gt;. Some of the code
made it’s way to &lt;a href=&quot;https://github.com/yrsIDAT&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;someone who had worked there. Amazing.
[^2]: We did have quite a few technical problems in demonstrating, which was
a pain. But, they did eventually get to demonstrate it at the end, after
installing Chrome over a dodgy 3G connection, and trying it on many different
laptops. But, it worked in the end.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;It was once a Birds custard factory. On the train back, I sat next to &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</summary>
    </entry>
    
    <entry>
        <title>Finishing at Rokk Media</title>
        <link href="https://nickcharlton.net/posts/finishing-at-rokk-media.html" />
        <id>https://nickcharlton.net/posts/finishing-at-rokk-media.html</id>
        <published>Sat, 04 Aug 2012 00:00:00 +0000</published>
        <updated>Sat, 04 Aug 2012 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;And, so, that is the end of my placement. The conclusion of a year of working
– and learning – is quite a sad one. I’ve really enjoyed it. I’ve been able to
work on a diverse range of projects, from web stuff across to several
iOS projects. And, ontop of that, working with a great group of people. I’ll
miss it quite a lot.&lt;/p&gt;

&lt;p&gt;There’s a lot to be said for being able to write code everyday for just over
a year. But, on top of this, get advice, support and different constraints from
what I’ve previously experienced on my own projects and those through
University.&lt;/p&gt;

&lt;p&gt;There are often pinacle points in your life where you go “yes, this was damn
important and influential”. This was one of them.&lt;/p&gt;

&lt;p&gt;So, thanks Adam, Tristan and the other people at Rokk Media. It was a great year.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Stuff I&apos;m Working On &amp;amp; Learning</title>
        <link href="https://nickcharlton.net/posts/stuff-im-working-on-learning.html" />
        <id>https://nickcharlton.net/posts/stuff-im-working-on-learning.html</id>
        <published>Sun, 24 Jun 2012 00:00:00 +0000</published>
        <updated>Sun, 24 Jun 2012 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;This morning, &lt;a href=&quot;http://rc3.org/&quot;&gt;Rafe&lt;/a&gt; &lt;a href=&quot;http://rc3.org/2012/06/23/stuff-i-needwant-to-spend-time-learning/&quot;&gt;posted about the stuff he’d like to be learning&lt;/a&gt;. So, I thought that was a nice way to remind myself all the stuff I’m currently working on and the stuff I’m attempting to learn, in various states of completion:&lt;/p&gt;

&lt;h3 id=&quot;projects&quot;&gt;Projects&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Bat Finder: Since the &lt;a href=&quot;http://nickcharlton.net/post/bats-hacks-and-fieldwork&quot;&gt;FSC Hack Day&lt;/a&gt;, I’ve been learning Core Audio and starting to understand audio analysis and visualisation. It’s hard stuff. It’ll be a while before I have anything significant, I suspect.&lt;/li&gt;
  &lt;li&gt;Predict the Sky: The end result of the &lt;a href=&quot;http://nickcharlton.net/post/nasa-space-apps-challenge-predict-the-sky&quot;&gt;NASA Space Apps Challenge at the Met Office&lt;/a&gt; back in April. We’re not &lt;em&gt;that&lt;/em&gt; far from a testable end product.&lt;/li&gt;
  &lt;li&gt;RepRap: Just over a month ago, I bought a Prusa Mendel RepRap Kit. I’ve since been slowly assembling and testing it. It’s intended to be for robotics prototyping, but I’m sure I’ll end up with endless printed trinkets along the way, too.&lt;/li&gt;
  &lt;li&gt;Quadcopter: This is intended to be the culumation of several of these projects and things I’m learning. It needs: electronics, control code&lt;sup&gt;&lt;a href=&quot;#footnote_quad_1&quot; id=&quot;identifier_quad_1&quot; class=&quot;footnote-link&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; and then I can look into swarming behaviour and mapping.&lt;/li&gt;
  &lt;li&gt;Environmental Monitoring: Energy usage. Common behaviours. What can we discover by logging everything over an academic year? This is a little project for October onwards.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;learning&quot;&gt;Learning&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Maths (matrices, compound event probability, calculus): All as a foundation to the rest of the stuff I’m doing. It’s often hard, but &lt;strong&gt;amazing&lt;/strong&gt;&lt;sup&gt;&lt;a href=&quot;#footnote_maths_2&quot; id=&quot;identifier_maths_2&quot; class=&quot;footnote-link&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
  &lt;li&gt;Statistics: &lt;a href=&quot;http://www.udacity.com/overview/Course/st101/CourseRev/1&quot;&gt;Udacity’s “Intro to Statistics”&lt;/a&gt; starts tomorrow. There’s data everywhere. We’re producing more at an exponential rate. The power is in those who can do stuff with it.&lt;/li&gt;
  &lt;li&gt;AI: As I go into my final year&lt;sup&gt;&lt;a href=&quot;#footnote_degree_3&quot; id=&quot;identifier_degree_3&quot; class=&quot;footnote-link&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;, this is everything. My current overall objective is to learn as much is humany possible to make next year go smoothly. I’ve been doing &lt;a href=&quot;https://www.ai-class.com/&quot;&gt;AI Class&lt;/a&gt; on-and-off over the past month.&lt;/li&gt;
  &lt;li&gt;iOS 6 &amp;amp; Mountain Lion Additions: I’ve spent the last year doing this. I still find it a fantastic platform to develop for. There is a ton of stuff added at the developer level. Lots of improved things, as well as new APIs to learn.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, yes. I like to keep myself busy. Some of this (the bat detection stuff, certainly) I do intend to document here (outside of textbooks, this kind of programming isn’t well written about). But most of that will come with time. Pester me if something is specifically interesting. Otherwise I assume it’s all easy to learn stuff.&lt;/p&gt;

&lt;hr /&gt;
&lt;ol class=&quot;footnotes&quot;&gt;
    &lt;li id=&quot;footnote_quad_1&quot;&gt;Whilst quadcopters are fantastically agile machines (they can be anywhere in a given space), they have no inherant stability. Just getting one to hover is the first challenge.&lt;a href=&quot;#identifier_quad_1&quot;&gt;↩&lt;/a&gt;&lt;/li&gt;
    &lt;li id=&quot;footnote_maths_2&quot;&gt;I can recommend &lt;a href=&quot;http://www.amazon.co.uk/gp/product/0199142432/ref=as_li_ss_tl?ie=UTF8&amp;amp;tag=nisbl-21&amp;amp;linkCode=as2&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=0199142432&quot;&gt;&quot;Understanding Pure Mathematics&quot; by Sadler and Thorning&lt;/a&gt; (which covers basic algebra, matrices and some calculus, etc.), which was kindly lent to me by a friend. But also &lt;a href=&quot;http://www.khanacademy.org/&quot;&gt;Khan Academy&lt;/a&gt;.&lt;a href=&quot;#identifier_maths_2&quot;&gt;↩&lt;/a&gt;&lt;/li&gt;
    &lt;li id=&quot;footnote_degree_3&quot;&gt;Holy shit. I&apos;ve nearly finished my degree.&lt;a href=&quot;#identifier_degree_3&quot;&gt;↩&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</summary>
    </entry>
    
    <entry>
        <title>Bats, Hacks &amp;amp; Fieldwork</title>
        <link href="https://nickcharlton.net/posts/bats-hacks-and-fieldwork.html" />
        <id>https://nickcharlton.net/posts/bats-hacks-and-fieldwork.html</id>
        <published>Tue, 29 May 2012 00:00:00 +0000</published>
        <updated>Tue, 29 May 2012 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Two weekends ago, I was in Slapton at the &lt;a href=&quot;http://field-studies-council.org/&quot;&gt;Field Studies Council&lt;/a&gt; for their first hack day. It aimed to bring together both people in education and those in technology and see what could be done when you bashed their two heads together. And it was bloody good.&lt;/p&gt;

&lt;p&gt;Set in the picturesque Devon countryside, a bunch of us turned up not quite knowing what to expect. For most, it’d been quite a while since Geography lessions in school, let alone any fieldwork.&lt;/p&gt;

&lt;p&gt;The hackday had been organised by &lt;a href=&quot;https://twitter.com/FolkPrincess&quot;&gt;Harriet White&lt;/a&gt; of the FSC, &lt;a href=&quot;http://reithian.blogspot.co.uk/&quot;&gt;Ant Miller&lt;/a&gt; of BBC R&amp;amp;D, and &lt;a href=&quot;http://about.me/johnbevan&quot;&gt;John Bevan&lt;/a&gt; of Mozilla. But we also had people from elsewhere in the BBC, a few associated with &lt;a href=&quot;http://rewiredstate.org/&quot;&gt;Rewired State&lt;/a&gt;, FSC tutors on hand and designers and developers of various afflictions. We were also lucky to have a few people from the &lt;a href=&quot;http://bristol.hackspace.org.uk/&quot;&gt;Bristol Hackspace&lt;/a&gt;, too.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;https://nickcharlton.net/resources/bats_hacks_fieldwork/pub.jpg&quot; width=&quot;500&quot; alt=&quot;The Tower — The Lovely Local Pub&quot; /&gt;
&lt;figcaption&gt;The Tower — The Lovely Local Pub.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;bat-detection&quot;&gt;Bat Detection&lt;/h3&gt;

&lt;figure&gt;
&lt;img src=&quot;https://nickcharlton.net/resources/bats_hacks_fieldwork/bat_guide.jpg&quot; width=&quot;500&quot; alt=&quot;The FSC&apos;s Guide to British Bats&quot; /&gt;
&lt;figcaption&gt;The FSC&apos;s Guide to British Bats.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;a href=&quot;http://mike.saunby.net/&quot;&gt;Mike&lt;/a&gt; and I worked on a low-cost bat detector. Mike had picked up an off-the-shelf bat detector and had been experimenting with outputting typical British bat tones (in the 40kHz to 110Khz range) using a multitude of different devices. In the end, he’d found that it was possible with expensive external sound cards and really cheap internal sound cards (we concluded that manufacturers are unlikely to ensure output is kept to a sensible range at the low end, but unlike the expensive stuff it certainly wouldn’t be optimised for being used like it.)&lt;/p&gt;

&lt;p&gt;This allowed us to simulate bat calls indoors, at any time. For us, this was vital. But has the additional effect of being mightily useful in the classroom. On a weekend of fieldwork, there would not be much chance for evenings spent hunting around for bats; a simulator could provide a demonstration of what it’ll be like using bat detectors and what you would be likely to discover.&lt;/p&gt;

&lt;p&gt;After experimenting with a few different bat detectors, we talked about what we could do to make bat finding more accessible. In the morning of the first day, &lt;a href=&quot;http://daviderogers.blogspot.co.uk/&quot;&gt;David Rogers&lt;/a&gt; of the &lt;a href=&quot;http://prioryschool.wordpress.com/&quot;&gt;Priory School&lt;/a&gt; in Portsmouth had talked about student devices and what he (and other teachers) had been doing to use them to aid learning. Whilst the &lt;abbr title=&quot;Bring Your Own Device&quot;&gt;BYOD&lt;/abbr&gt; model seems slightly problematic for our case&lt;sup&gt;&lt;a href=&quot;#footnote_mobile_1&quot; id=&quot;identifier_byod_1&quot; class=&quot;footnote-link&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, a universal bit of hardware seemed a good place to start.&lt;/p&gt;

&lt;p&gt;Off the shelf bat detectors use frequency shifting to take the bat’s ultrasonic clicks and squeaks and turn it into human audible frequencies. In the UK, bats are typically somewhere around 45Khz. (Human hearing is somewhere between 20Hz-20Khz.) Fortunately, doing this is quite typical (its how transistor radios work) and relatively cheap to do. So, with this in mind, we came up with the idea of a &lt;a href=&quot;https://squareup.com/square&quot;&gt;Square&lt;/a&gt; like dongle which is inserted into the audio input of a mobile device. The device can then “listen” in to the audio signal and provide some sort of output.&lt;/p&gt;

&lt;p&gt;Mike spent the rest weekend working on the circuit, and quite quickly got something working. He then iterated over it to attempt to reduce the amount of components, and try different microphones to see which was best. &lt;a href=&quot;http://mike.saunby.net/&quot;&gt;I’m sure he’ll blog about it soon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the mean time, I looked into how we could analyse and present the audio data as it came in, and what the audio range of the iPhone&lt;sup&gt;&lt;a href=&quot;#footnote_platforms_2&quot; id=&quot;identifier_platforms_2&quot; class=&quot;footnote-link&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; was like. I didn’t go much further with looking at the audio range, other than concluding that it was at least slightly outside of standard Human hearing range, but seemingly not up to ultrasonic.&lt;/p&gt;

&lt;p&gt;I quickly discovered that audio programming is hard. Or at least, low-level audio programming is. Exploring the lower-level &lt;a href=&quot;https://developer.apple.com/library/ios/#documentation/MusicAudio/Conceptual/CoreAudioOverview/Introduction/Introduction.html&quot;&gt;Core Audio APIs&lt;/a&gt; was a little beyond hacking at a weekend. I did however recall reading about &lt;a href=&quot;http://alexbw.github.com/novocaine/&quot;&gt;Novacaine&lt;/a&gt; and a few other audio recording projects which kickstarted pulling in audio data.&lt;/p&gt;

&lt;p&gt;I was quite surprised to find that graphing the data was also not easy. With libraries for most things, you assume it’ll be easy to put together a time-series graph of live data. Whilst the likes of &lt;a href=&quot;https://code.google.com/p/core-plot/&quot;&gt;Core Plot&lt;/a&gt; exist, it’s not as elegant as it could be. Subsequently I’ve spent last weekend working on something to solve it. It’s common for bat detectors to use frequency-histograms to show ‘loud’ points in the past over various frequencies at once, as this helps with detection, too. So I also need to work out how to do that. Graphing additionally comes with the advantage of being far more accessible than an audio output. We’re already shifting sounds that we, as humans are unable to hear, so why do we limit it to outputting just human audible sound?&lt;/p&gt;

&lt;p&gt;Handily, Novacaine is extracted from a few audio analysis projects which in the end I used to demonstrate what we’d found. On the concluding part on the Sunday, the attendants, organisers and judges came around to each lab to see what we’d done. After Mike had run through the basics of bat detection, and the circuit that he’d put together, we demonstrated what could be done using an iPad and &lt;a href=&quot;http://itunes.apple.com/us/app/oscope/id344345859?mt=8&quot;&gt;oScope&lt;/a&gt;. You could clearly see the bat calls on the oscilloscope display, as played through a cheap netbook and the speakers in the room.&lt;/p&gt;

&lt;p&gt;It also turned to demonstrate the phenomena of &lt;a href=&quot;http://en.wikipedia.org/wiki/Presbycusis&quot;&gt;presbycusis&lt;/a&gt; or age-related hearing loss, as the room speakers spread across the audial-range of the bat recordings more than on the netbook did on its own. People under the age of ~35 were unable to hear this noise, but those below could. (It’s like a very high-pitched, uncomfortable drone.) We also discovered that modern (especially cheap) digital electronics are remarkably noisy in the ultrasonic range. Passing one of the bat detectors over certain devices in various different ranges lead to a lot of background noise.&lt;/p&gt;

&lt;h3 id=&quot;winning-an-award&quot;&gt;Winning an Award&lt;/h3&gt;

&lt;p&gt;We were in Lab 1 and went first. After we’d done our demonstration, we went through the other labs to see what had been done. Everyone did something fantastic. From 3D printed representations of beach profiles, to Arduino powered data logging and applications for collecting data in the field, to tools for anaylising that data back in the classroom. Everyone attempted to solve — with a sane use of technology — the problems that the team have at the FSC.&lt;/p&gt;

&lt;p&gt;On the blog, &lt;a href=&quot;http://fschackday.wordpress.com/2012/05/22/fsc-hack-winners/&quot;&gt;Ant has written up about all of the projects and the winners&lt;/a&gt;. Which you should go and read.&lt;/p&gt;

&lt;p&gt;Done that? Good. Well, there were four awards. And we won one of them!&lt;/p&gt;

&lt;p&gt;We won the “People’s Choice Award”, awarded by putting stones into tubs after we’d all looked at the different projects. It was extremely satisfying to gain the approval of all of the other people there. It was fantastic to see so many people interested in what we had been doing.&lt;/p&gt;

&lt;h3 id=&quot;documeting-it-all&quot;&gt;Documeting it All&lt;/h3&gt;

&lt;p&gt;Before and during the weekend we had &lt;a href=&quot;http://rowstar.blogspot.co.uk/&quot;&gt;Rowan Stanfield&lt;/a&gt; and others documenting the event. I’m pretty sure I haven’t seen it done to this much detail before. Looking back it was quite a marvellous idea. The whole weekend is laid out for us to look back onto, as well as for others to see what was done. It provides a fantastic documented legacy of the whole event.&lt;/p&gt;

&lt;p&gt;You can see all of this on the &lt;a href=&quot;http://fschackday.wordpress.com/&quot;&gt;Fieldwork Hackday&lt;/a&gt; site.&lt;/p&gt;

&lt;h3 id=&quot;result-showcase-event&quot;&gt;Result Showcase Event&lt;/h3&gt;

&lt;p&gt;On Wednesday there is a &lt;a href=&quot;http://lanyrd.com/2012/fschackresults/&quot;&gt;wrap up/project show off event up in London&lt;/a&gt; at the Mozilla Space. I’ll be there presenting the bat stuff (with hardware, too!)&lt;/p&gt;

&lt;p&gt;I understand it’s going to be streamed — as well as showing off the video that was produced of the event. I’ll write up something else after the event, probably with more pictures.&lt;/p&gt;

&lt;hr /&gt;
&lt;ol class=&quot;footnotes&quot;&gt;
    &lt;li id=&quot;footnote_byod_1&quot;&gt;It&apos;s hard plan out where you dedicate resources to an almost unlimited amount of possible devices. Especially on a project like this. But, more importantly, it&apos;s unfair to over emphasise finances where we want people learning.&lt;a href=&quot;#identifier_byod_1&quot;&gt;↩&lt;/a&gt;&lt;/li&gt;
    &lt;li id=&quot;footnote_platforms_2&quot;&gt;Given I&apos;ve spent the last year doing iOS development it was the right thing to start on. But, there&apos;s absolutely no reason why it wouldn&apos;t work elsewhere too.&lt;a href=&quot;#identifier_platforms_1&quot;&gt;↩&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</summary>
    </entry>
    
    <entry>
        <title>NASA Space Apps Challenge &amp;amp; Predict the Sky</title>
        <link href="https://nickcharlton.net/posts/nasa-space-apps-challenge-predict-the-sky.html" />
        <id>https://nickcharlton.net/posts/nasa-space-apps-challenge-predict-the-sky.html</id>
        <published>Sun, 06 May 2012 00:00:00 +0000</published>
        <updated>Sun, 06 May 2012 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Two weekends ago I was at the &lt;a href=&quot;http://metoffice.gov.uk/&quot;&gt;Met Office&lt;/a&gt; for the &lt;a href=&quot;http://spaceappschallenge.org/&quot;&gt;NASA International Space Apps Challenge&lt;/a&gt;. It was quite a fantastic weekend. Four or so teams of people worked on a set of preset challenges (suggested by the leaders of those teams in the weeks running up to the event) and the rest of us congregated around the group we were most interested in.&lt;/p&gt;

&lt;p&gt;The group I was in worked on a project which we dubbed ‘Predict the Sky’. The idea was to take astronomical passovers and combine that with weather data to provide a useful indication of what you would be able to see in the night sky and it worked remarkably well. We ended up implementing a web service, iOS &amp;amp; Android applications and began on a web frontend. &lt;a href=&quot;http://vimeo.com/40825160&quot;&gt;At the end we demonstrated it using a video walkthrough of the two applications and sections of the design process&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We had a fantastic team (consisting of: &lt;a href=&quot;https://twitter.com/bushbaby2511&quot;&gt;Tom Bell&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/sophiedennis&quot;&gt;Sophie Dennis&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/robjglover&quot;&gt;Rob Glover&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/ehibling&quot;&gt;Emma Hibling&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/stevehadd&quot;&gt;Stephen Haddad&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/angerelle&quot;&gt;Angela Relle&lt;/a&gt; &amp;amp; &lt;a href=&quot;http://adamretter.org.uk/&quot;&gt;Adam Retter&lt;/a&gt;.), who were all a pleasure to work with. Quite a few people commented on both how organised and on the amount we achieved in the time. I couldn’t have had a better team of people to work with.&lt;/p&gt;

&lt;p&gt;The other teams &amp;amp; challenges were pretty fantastic too. The winners of the event where &lt;a href=&quot;http://spaceappschallenge.org/challenge/growers-nation/&quot;&gt;Growers Nation&lt;/a&gt; and &lt;a href=&quot;http://spaceappschallenge.org/challenge/welovedata-challenge/&quot;&gt;We Love Data&lt;/a&gt;. The former aims to crowdsource crop growing information, combined with climate and soil data to give the best indication on when crops should be planted. We Love Data aimed to give a bunch of demonstrations on what could be done with data “in the real world” using a bunch of Arduino’s and related hardware. They produced a rather impressive wave machine using the stream running through the “Street” in the middle in the Met Office building that would activate and push through a floating “ISS Duck” when the International Space Station would pass over. It worked rather well on the Saturday night. They also produced a clever 3D-printed pollen grain which would colour according to Met Office-provided pollen counts. The idea behind this was to provide smart, internet connected objects which wouldn’t necessarily be connected to a computer. These kind of products (much like the wider &lt;a href=&quot;http://en.wikipedia.org/wiki/Internet_of_Things&quot;&gt;Internet of Things&lt;/a&gt; concept) have the power to open up data (like pollen counts) to those who wouldn’t want a ‘widget’ or application to handle it on a more general purpose computer. &lt;a href=&quot;http://www.interactivewearables.co.uk/?p=96&quot;&gt;Dougie Kinnear, one of the team members produced a rather comprehensive write up&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Over the weekend we got impressively far through the project. But not quite enough to release it. On the iOS side (the bit I worked on) I got the stage where we just needed to wire up the network request to the view. It looks like we’ll be doing some sort of workshop-like session in the next few weeks to iron out the rest and figure out where we see it in the future.&lt;/p&gt;

&lt;p&gt;However. If you’d like to look it at now. &lt;a href=&quot;https://github.com/MetOfficeSpaceApps/PredictTheSky&quot;&gt;It’s all on GitHub&lt;/a&gt;. &lt;a href=&quot;http://www.adamretter.org.uk/blog/entries/nasa-space-apps.xml&quot;&gt;Adam also wrote up what happened&lt;/a&gt;.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>ORGCon 2012</title>
        <link href="https://nickcharlton.net/posts/orgcon-2012.html" />
        <id>https://nickcharlton.net/posts/orgcon-2012.html</id>
        <published>Thu, 29 Mar 2012 00:00:00 +0000</published>
        <updated>Thu, 29 Mar 2012 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;On Saturday, I was at the &lt;a href=&quot;http://www.openrightsgroup.org/&quot;&gt;Open Rights Group&lt;/a&gt; Conference at the University of Westminster in London. It was a very good day, full of interesting talks from the likes of Cory Doctorow, Wendy Seltzer and Laurance Lessig. It was the first time I’ve seen these talk in person (Lessig was notably impressive - especially with his slides.) Below are some of the notes I made.&lt;/p&gt;

&lt;h3 id=&quot;cory-doctorow-the-coming-war-on-general-purpose-computing&quot;&gt;Cory Doctorow: The Coming War on General Purpose Computing&lt;/h3&gt;

&lt;p&gt;This was about the progression towards specific computers - not ones which can crunch any numbers that you throw at it, but tailored and locked down to a specific purpose.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Original ”early” DRM -&amp;gt; Easy to break. Broken because it was more convenient with it removed.&lt;/li&gt;
  &lt;li&gt;The early information age was very much a ”charge for all the things” charging module. Skipping, pausing, etc.&lt;/li&gt;
  &lt;li&gt;DRM is exponentially problematic, one use opens up many more problems and as you solve those the problems just get bigger.&lt;/li&gt;
  &lt;li&gt;Complete failure of the UN’s (WIPO) copyright laws.
    &lt;ul&gt;
      &lt;li&gt;they posed unrealistic demands upon reality.&lt;/li&gt;
      &lt;li&gt;it’s a good example of a worldwide treaty which solves a  of the wrong problems.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Law makers represent populations of people, not subject areas. They use heuristics to balance good rules for the population.&lt;/li&gt;
  &lt;li&gt;But these heuristics utterly fail for technology.&lt;/li&gt;
  &lt;li&gt;General Purpose Computing is much like the wheel is to the car.&lt;/li&gt;
  &lt;li&gt;As control is aimed at General Purpose Computing, appliances look like a solution.&lt;/li&gt;
  &lt;li&gt;Unfortunately, this opens up more issues: SOPA, Human Rights, etc.&lt;/li&gt;
  &lt;li&gt;Copyright however is usually considered less economically important (certainly in comparison to food, water, etc), even though computers now essentially make up everything around us.&lt;/li&gt;
  &lt;li&gt;Every big industry will turn to the easy solution of locking down General Purpose Computing.&lt;/li&gt;
  &lt;li&gt;Thus, the fight will only get harder.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This talk was an updated version of a previous one, and also &lt;a href=&quot;http://boingboing.net/2012/01/10/lockdown.html&quot;&gt;an article on BoingBoing&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;wendy-seltzer-organising-for-the-open-net&quot;&gt;Wendy Seltzer: Organising for the Open Net&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;With SOPA/PIPA, representatives finally saw the power of the Internet.&lt;/li&gt;
  &lt;li&gt;There is a long history of stupid, naïve attempts at legislation in the States (&lt;a href=&quot;http://en.wikipedia.org/wiki/Higher_Education_Opportunity_Act#2008_reauthorization&quot;&gt;HEOA&lt;/a&gt;, &lt;a href=&quot;http://en.wikipedia.org/wiki/Combating_Online_Infringement_and_Counterfeits_Act&quot;&gt;COICA&lt;/a&gt;.)&lt;/li&gt;
  &lt;li&gt;You cannot control the Internet without fundamentally breaking it.
    &lt;ul&gt;
      &lt;li&gt;the Internet would not have grown to be what it is with significant control.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;And, how can it be right that one Government Department can prop up a dying business model?
    &lt;ul&gt;
      &lt;li&gt;whilst one part of the US Government was praising the freedom of speech aspect, others were going in the opposite direction.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;The power of the hivemind for real world events cannot be understated. Even the effect that changing a Twitter avatar has is greater than one would expect. (Even if it does appear to be “&lt;a href=&quot;http://en.wikipedia.org/wiki/Slacktivism&quot;&gt;slactivism&lt;/a&gt;”)&lt;/li&gt;
  &lt;li&gt;The “white blood cell”/vaccine metaphor fits quite well. But, can following such a model actually make us more resilient against future issues?&lt;/li&gt;
  &lt;li&gt;But an immune system can also kill itself if it overreacts. We (as the internet), have to be careful to not cry wolf at the next piece of legislation to rear its head.&lt;/li&gt;
  &lt;li&gt;Motivation to keep fighting can and will come from the very specific niche interest that makes the general purpose Internet amazing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;panel-is-all-this-data-doing-us-any-good&quot;&gt;Panel: Is all this data doing us any good?&lt;/h3&gt;

&lt;p&gt;This session was a panel, with &lt;a href=&quot;https://twitter.com/#!/CountCulture&quot;&gt;Chris Taggart&lt;/a&gt; of &lt;a href=&quot;http://OpenCorporates.com/&quot;&gt;OpenCorporates&lt;/a&gt;, &lt;a href=&quot;http://rufuspollock.org/&quot;&gt;Rufus Pollock&lt;/a&gt; (of Open Knowledge Foundation) and &lt;a href=&quot;http://heatherbrooke.org/&quot;&gt;Heather Brooke&lt;/a&gt; (who worked on uncovering the MP’s expenses scandal). It was split into questions, but some notes ended up merged into others.&lt;/p&gt;

&lt;h4 id=&quot;who-are-we-empowering&quot;&gt;Who are ”we” empowering?&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Only about 1% of all Government data (in the UK) is being opened up.&lt;/li&gt;
  &lt;li&gt;That is mostly because it is sold, and a business model has been built around it.&lt;/li&gt;
  &lt;li&gt;Stops Innovation. “If your business model doesn’t match, you can’t play.”&lt;/li&gt;
  &lt;li&gt;Closed data is growing exponentially, far faster than that which is being open.&lt;/li&gt;
  &lt;li&gt;We’re empowering everyone. But, the small guy has far more power even with smaller resources.&lt;/li&gt;
  &lt;li&gt;The American journalism model relies upon public records. In the UK, we don’t have this. Unfortunately, this leads to a patronage network - making it harder for new people to get in.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;value&quot;&gt;Value&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Governments may very well agree with opening up data, but it is expensive (it needs to be organised, packaged, etc.)&lt;/li&gt;
  &lt;li&gt;Government need to understand the bigger social value other than the direct business profit.&lt;/li&gt;
  &lt;li&gt;However; data without tools isn’t useful.
    &lt;ul&gt;
      &lt;li&gt;tools, analytics, etc is a resource problem.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;“Pirate” Data Sets are good and bad.
    &lt;ul&gt;
      &lt;li&gt;The MPs expenses leak was good - it raised public awareness of an ongoing issue.&lt;/li&gt;
      &lt;li&gt;The Wikileaks “cablegate” wasn’t good - it hurt government’s opinions on transparency (not everything is appropriate open.)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Journalists need tools and education to be able to utilise data for the “common man”. Just opening up stuff isn’t useful.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;opening-up-data&quot;&gt;Opening Up Data&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;It’s a politcal, rather than legal issue.
    &lt;ul&gt;
      &lt;li&gt;even though it might well be against usage terms and conditions, organisations are often happy to ignore them to see people doing good things with it.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;The ease of getting data opened is somewhat because someone isn’t fighting the other way (unlike copyright, for example.)&lt;/li&gt;
  &lt;li&gt;Do it, appologise later. “Generally” not much has happened to people doing precisely that.&lt;/li&gt;
  &lt;li&gt;A good way forward would be “open by default” - no more Crown Copyright, for example.&lt;/li&gt;
  &lt;li&gt;But, people need to ask for data that they wish to use - organisations may just assume people aren’t interested otherwise.&lt;/li&gt;
  &lt;li&gt;And, uncovering something like corruption because of open data wouldn’t hurt at all.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;ross-anderson-how-secure-is-the-anonymisation-of-open-data&quot;&gt;Ross Anderson: How Secure is the Anonymisation of Open Data?&lt;/h3&gt;

&lt;p&gt;This session was given by &lt;a href=&quot;http://www.cl.cam.ac.uk/~rja14/&quot;&gt;Ross Anderson, a Security Professor at Cambridge Computer Lab&lt;/a&gt;. It mostly focused upon medical data, and the information than can be inferred from disparate data sets.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Back in 1979, it was shown that you can find more data than anyone had previously expected in US Census Data.&lt;/li&gt;
  &lt;li&gt;In the ‘90’s, it was shown that a State Senator could be identified through health data.&lt;/li&gt;
  &lt;li&gt;The same happened in the UK with NHS data.&lt;/li&gt;
  &lt;li&gt;Generally, if you know enough information about a place (e.g. Cambridge Computer Lab.) you can easily glean a lot from publicly available data (for example, there is only one female professor, and the University publish average salary statistics.)&lt;/li&gt;
  &lt;li&gt;It is very hard to stop data leakage through inference unless you know all of the cases where it can be “attacked”.&lt;/li&gt;
  &lt;li&gt;The more data that is opened up (inc. social network data), the more patterns that can be seen.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;mozilla-do-not-track&quot;&gt;Mozilla: Do Not Track&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Aims to give users a universal, simple opt-out from advertising based data collection.&lt;/li&gt;
  &lt;li&gt;It is being standardised, by consensus by the W3C (from a mix of privacy advocates, advertisers and governments.)&lt;/li&gt;
  &lt;li&gt;Implemented as an HTTP Header, and an accompanying JSON status file.&lt;/li&gt;
  &lt;li&gt;In general, the policies define the levels of sharing between first and third parties on a given website (e.g.: a Twitter “Tweet” button is a third party to the New York Time’s site, until you click on it.)&lt;/li&gt;
  &lt;li&gt;It also defines “outsource” providers for analytics and CDNs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My initial thought with this was about it’s “opt-out” nature and requirement that organisations implement it at their end. The well-behaving organisations are likely happy to be involved and implement it - but they’re probably already not doing crappy things with data collected from our browsing sessions.&lt;/p&gt;

&lt;p&gt;You can read more about &lt;a href=&quot;http://dnt.mozilla.org/&quot;&gt;Mozilla’s Do Not Track Project here&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-upcoming-data-protection-act-changes&quot;&gt;The Upcoming Data Protection Act Changes&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;The original, “business friendly” &lt;a href=&quot;http://en.wikipedia.org/wiki/Data_Protection_Act_1998&quot;&gt;Data Protection Act&lt;/a&gt; will be (in about four years) replaced.&lt;/li&gt;
  &lt;li&gt;In general, this will balance out the laws throughout the EU.&lt;/li&gt;
  &lt;li&gt;Industry should see less red-tape.&lt;/li&gt;
  &lt;li&gt;Notably however, instead of two sets of data being considered seperate (where one could be identified from the merging of the two), this would no longer be allowed. [See “How Secure is the Anonymisation of Open Data?, above.]&lt;/li&gt;
  &lt;li&gt;What defines “personal data” is being redefined (e.g.: IP addresses, cookies, etc.). Some of this is still undefined.&lt;/li&gt;
  &lt;li&gt;It is also possible that there will be a clause requiring individuals to request data from companies that is stored about them.&lt;/li&gt;
  &lt;li&gt;And it is also possible that there will be a clause requiring companies to comply with user’s requests to remove data stored about them (this may cause issues for search engines, social networks, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;laurence-lessig-recognizing-the-fight-were-in&quot;&gt;Laurence Lessig: Recognizing The Fight We’re In&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://www.lessig.org/&quot;&gt;Lessig&lt;/a&gt; is a fantastic speaker. This talk was an outcry to release how important the fight for which the ORG stands for is. It spreads from the model of spectrum allocation for managing radio standards to corruption in modern politics.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Spectrum allocation (and property) only works on big, wide broadcasts, not the small scale usage we generally see now (e.g.: wifi, bluetooth, etc.)&lt;/li&gt;
  &lt;li&gt;Why can’t the TCP/IP model work?
    &lt;ul&gt;
      &lt;li&gt;defined as scalable usage with users, rather than applying scarcity.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;It stays as property because of lobbyist.
    &lt;ul&gt;
      &lt;li&gt;We need to rememeber to askt he question: “Who makes money from this?”.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Poor broadbad, copyright extensions &amp;amp; SOPA insanity is all driven by greed.&lt;/li&gt;
  &lt;li&gt;Fundamental copyright is necessary. However, it needs to benefit the creators &amp;amp; authors - not publishers.&lt;/li&gt;
  &lt;li&gt;Locking up academic information in exclusivity is an ethical problem.
    &lt;ul&gt;
      &lt;li&gt;How can it be right that publically funded research makes money for private publishing organisations?&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;The state of US politics is a kind of corruption - relative to the baseline on which it was founded.&lt;/li&gt;
  &lt;li&gt;Most people do not vote because they have no belief in the system.
    &lt;ul&gt;
      &lt;li&gt;it is thought that corporate interests rule the way and that voting alone is pointless.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;The growth of citizen politics is leading to an international anti-corruption movement. This is a fantastic thing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can watch &lt;a href=&quot;https://vimeo.com/39188615&quot;&gt;the video of Lessig’s talk here&lt;/a&gt;.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Git Workshop</title>
        <link href="https://nickcharlton.net/posts/git-workshop.html" />
        <id>https://nickcharlton.net/posts/git-workshop.html</id>
        <published>Mon, 12 Mar 2012 00:00:00 +0000</published>
        <updated>Mon, 12 Mar 2012 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;On Saturday, &lt;a href=&quot;http://thisisthechris.co.uk/&quot;&gt;Chris Hunt&lt;/a&gt; and I gave a workshop on Git at Digpen IV in Exeter. It seemed to go rather well.&lt;/p&gt;

&lt;p&gt;In fact, the whole day was excellent. An afternoon of interesting, detailed and well choreographed sessions. You can find a collection of them over on &lt;a href=&quot;http://lanyrd.com/2012/digpen/&quot;&gt;Lanyrd&lt;/a&gt;. Most have slides and there’s videos, too.&lt;/p&gt;

&lt;p&gt;The slides can be found on &lt;a href=&quot;http://speakerdeck.com/u/nickcharlton/p/getting-started-with-source-control-and-git&quot;&gt;Speaker Deck&lt;/a&gt;. There’s a &lt;a href=&quot;http://github.com/nickcharlton/git-workshop&quot;&gt;repository over on GitHub&lt;/a&gt; with the rest of the resources.&lt;/p&gt;

&lt;p&gt;I gather I also agreed to do a follow on session at a future one. So, with that in mind, shout if you have something in mind that you’d like covered.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Review: Hacking and Securing iOS Applications by Jonathan Zdziarski</title>
        <link href="https://nickcharlton.net/posts/review-hacking-and-securing-ios-applications.html" />
        <id>https://nickcharlton.net/posts/review-hacking-and-securing-ios-applications.html</id>
        <published>Sun, 26 Feb 2012 00:00:00 +0000</published>
        <updated>Sun, 26 Feb 2012 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I started this just after the &lt;a href=&quot;http://nickcharlton.net/post/mobile-security&quot;&gt;Path fiasco&lt;/a&gt;. It seemed timely to brush up on my security knowledge, especially for iOS intricacies, recommended practices and understanding obvious flaws. At the same time, O’Reilly had published &lt;a href=&quot;http://shop.oreilly.com/product/0636920023234.do&quot;&gt;“Hacking and Securing iOS Applications” by Jonathan Zdziarski&lt;/a&gt;, so I got a copy. This book provides exactly what I wanted; it’s a great next-step if you’ve been developing for iOS or the Mac for a while. Fortunately it assumes that, which allows the book to quickly jump into examples and solutions.&lt;/p&gt;

&lt;p&gt;After debunking some common myths, the book delves into pushing code onto a jailbroken device. It was quite an eye opener to see how simple it was (this isn’t something I’ve done since back on iPhone OS 1.3.3, or so.) If you can compile something using gcc, you can just about as easily (and quickly) push something onto a device.&lt;/p&gt;

&lt;p&gt;Related to the Path fiasco, the next fuller example (in Chapter 2) is about pushing the Address Book over the network. I found that quite amusing.&lt;/p&gt;

&lt;p&gt;The book then descends into exploiting the filesystem, and other common attack vectors. This is followed by sections on manipulating the Objective-C runtime and examples in applications which are the time of writing claimed to be “secure”, but suffered from simple to discover flaws.&lt;/p&gt;

&lt;p&gt;The second half of the book delves into advice on writing secure applications. Its “now you know what you can do, here’s how to engineer around it” style works fantastically and provides the most value - especially if you’re building something which is security conscious. Notable here was the chapter on encryption. It covered implementing SSL and flaws relating to it, as well as delving into using public-key encryption along with SSL when passing data around.&lt;/p&gt;

&lt;p&gt;After this, the book delves into ways to obfuscate methods and protect the data the application is working with. For example, providing traps which when executed would erase any useful encryption keys, or phone home (passing logs and/or GPS coordinates) to help mitigate any knock-on effects of a breached application. Some of these security holes are due to the reflective nature of Objective-C, which allows you to modify the runtime as it is executing - catching tampering attempts, or placing honey traps for attackers can be used as another line of defence.&lt;/p&gt;

&lt;p&gt;But, more importatly, the book aims to bring across a fundamental of security and penetration testing: You need to think and act like an attacker to see potential flaws and attacks. For this it is organised well, and because of that, it’s a great position to leap off from.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;O’Reilly provided a copy of the book for to review for free, as part of their Bloggers Programme.&lt;/em&gt;&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Mobile Security: I Don&apos;t Even Know Where to Begin</title>
        <link href="https://nickcharlton.net/posts/mobile-security.html" />
        <id>https://nickcharlton.net/posts/mobile-security.html</id>
        <published>Mon, 13 Feb 2012 00:00:00 +0000</published>
        <updated>Mon, 13 Feb 2012 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;With the &lt;a href=&quot;http://mclov.in/2012/02/08/path-uploads-your-entire-address-book-to-their-servers.html&quot;&gt;recent Path debacle&lt;/a&gt;, the security and privacy practices of various mobile application developers has come to light. Generally, this has focused around the use of Address Book data; here, developers are balancing violating individual privacy with easier connections - the metric of importance for such apps. On its own, this is fine — providing you have the user’s permission — the issue is in how developers are going about implementing it.&lt;/p&gt;

&lt;p&gt;Security is a numbers game. Nothing is or will ever be perfectly secure&lt;sup&gt;&lt;a href=&quot;#footnote_mobile_1&quot; id=&quot;identifier_mobile_1&quot; class=&quot;footnote-link&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, but the whole notion of attempting to protect users seems to have been entirely ignored. In most cases (Path, for example) this is just blind ignorance, but &lt;a href=&quot;http://mybroadband.co.za/news/cellular/43301-zing-mobile-messaging-app-all-the-details.html&quot;&gt;others seem to be actively avoiding such simple things as hashing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’m not sure which is worse. Is it worse that developers simply did not consider encrypting the data they were passing around, or another developer actively decided against it? At least if users know that their data is not being encrypted along the wire, and is being stored in plain text they can make a decision about whether or not to use it&lt;sup&gt;&lt;a href=&quot;#footnote_mobile_2&quot; id=&quot;identifier_mobile_2&quot; class=&quot;footnote-link&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. I suspect the bigger issue is education. The open nature of the Web is a fantastic thing and to some extent this is transferring to mobile development, too.&lt;/p&gt;

&lt;p&gt;But this means that developers do not have a well rounded conceptial understanding of what they are doing. Developers like Matt Gemmell shouldn’t have to write articles like: “&lt;a href=&quot;http://mattgemmell.com/2012/02/11/hashing-for-privacy-in-social-apps/&quot;&gt;Hashing for privacy in social apps&lt;/a&gt;” and &lt;a href=&quot;http://twitter.com/mattgemmell/status/169039433226649600&quot;&gt;receive responses from other developers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On iOS, the Address Book API calls have almost entirely been pulled over from the Mac. Only the picker tool is Cocoa, the rest is Core Foundation (C). I suspect the lack of user permissions is because of this, rather than any malicious intent. &lt;a href=&quot;https://developer.apple.com/appstore/guidelines.html&quot;&gt;Regardless, it’s against the developer agreement&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I certainly don’t think that a CS degree should be a prerequisite  for building any software; but somewhere we, as developers need to build up interest into. Just throwing in a little bit of hashing and salting at the end of a project cannot be a valid security standpoint to take for software that is used by a wide variety of users.&lt;/p&gt;

&lt;ol class=&quot;footnotes&quot;&gt;
    &lt;li id=&quot;footnote_mobile_1&quot;&gt;In iOS&apos;s case, whilst the filesystem is encrypted, it&apos;s still like keeping your house keys under your doormat. You cannot unlock the filesystem without also shipping the keys with the device. &lt;a href=&quot;#identifier_mobile_1&quot;&gt;↩&lt;/a&gt;&lt;/li&gt;
    &lt;li id=&quot;footnote_mobile_2&quot;&gt;But I don&apos;t think this is a good excuse. As app developers it is our, as much as the platform providers’, responsibility to protect our users. &lt;a href=&quot;#identifier_mobile_2&quot;&gt;↩&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</summary>
    </entry>
    
    <entry>
        <title>Brussels &amp;amp; FOSDEM 2012</title>
        <link href="https://nickcharlton.net/posts/brussels-fosdem-2012.html" />
        <id>https://nickcharlton.net/posts/brussels-fosdem-2012.html</id>
        <published>Sun, 12 Feb 2012 00:00:00 +0000</published>
        <updated>Sun, 12 Feb 2012 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Last weekend, I was in Brussels for &lt;a href=&quot;http://fosdem.org/2012/&quot;&gt;FOSDEM&lt;/a&gt;. It was an excellent weekend. I didn’t go too that many sessions at FOSDEM, but the ones I did go to were pretty good and have pushed me into finding out a few more things in detail.&lt;/p&gt;

&lt;p&gt;The first session I went to was titled &lt;a href=&quot;http://fosdem.org/2012/schedule/event/javalinuxapps&quot;&gt;“Why Java for Linux applications?”&lt;/a&gt;. It was interesting to see someone’s approach to implementing something that needed to be cross-platform, but I find both the development environment and the eventual product of &lt;a href=&quot;http://en.wikipedia.org/wiki/Swing_(Java)&quot;&gt;Swing&lt;/a&gt; hard to take as a recommendation. The next session was &lt;a href=&quot;http://fosdem.org/2012/schedule/event/debian_packaging&quot;&gt;“Debian packaging for beginners”&lt;/a&gt;. I’ve done some repackaging before, but nothing completely from scratch. It was interesting to see the process done, it’s not as complex as it first makes out. After these, I headed over to the GNUstep Devroom.&lt;/p&gt;

&lt;p&gt;Since spending a large amount of time developing for iOS, and some for the Mac, I’m obviously heavily invested in Objective-C and Cocoa. The &lt;a href=&quot;http://fosdem.org/2012/schedule/event/new_objc_features&quot;&gt;talk on the newer features of Obj-C&lt;/a&gt;, by &lt;a href=&quot;http://www.cs.swan.ac.uk/~csdavec/&quot;&gt;David Chisnall&lt;/a&gt; was especially interesting. Support for the likes of Automatic Reference Counting and blocks is far more comprehensive than I expected, and in some cases far better than Apple’s own implentation. I’ll be interested to see how the &lt;a href=&quot;http://etoileos.com/&quot;&gt;Étoilé&lt;/a&gt; project and others progress.&lt;/p&gt;

&lt;p&gt;On the other hand, the final talk of the day, &lt;a href=&quot;http://fosdem.org/2012/schedule/event/quantumstep_future&quot;&gt;QuantumSTEP&lt;/a&gt; was less impressive. It provides an implementation of Objective-C and Cocoa for the &lt;a href=&quot;http://www.quantum-step.com/&quot;&gt;GTA05 Open Source Phone platform&lt;/a&gt;. It seemed to be a little too much about slowly chasing up iOS, which itself is moving rather fast. It was interesting, though.&lt;/p&gt;

&lt;p&gt;On the Sunday, I primarily spent time in the Virtualisation &amp;amp; Cloud Devroom. I had previously heard about &lt;a href=&quot;http://www.ovirt.org/&quot;&gt;oVirt&lt;/a&gt;, but without an official release hadn’t looked too deeply into it. The introduction talk, &lt;a href=&quot;http://fosdem.org/2012/schedule/event/ovirt_intro&quot;&gt;“Virtualization Management the oVirt way”&lt;/a&gt; and the subsequent talks outlined the architecture that was being implemented.&lt;/p&gt;

&lt;p&gt;oVirt is intended to be an open source version of &lt;a href=&quot;http://www.vmware.com/products/vsphere/mid-size-and-enterprise-business/overview.html&quot;&gt;VMware’s vSphere&lt;/a&gt; product, but using &lt;a href=&quot;http://en.wikipedia.org/wiki/Kernel-based_Virtual_Machine&quot;&gt;KVM&lt;/a&gt;. The goal here is to provide a managed environment for running VMs, either for servers or “virtual desktops” for end users. This arrangement provides failover and general management on the server, rather than at the application level - something more common with traditional, non-web application infrastructure.&lt;/p&gt;

&lt;p&gt;Unfortunately, it seemed a brittle solution to the problem. I’m not a fan of projects that are made up of many complex interconnecting parts to form a whole. At least not when they depend on a core central component. There also seemed to be a little confusion in the Q&amp;amp;A about what would happen if you upgraded the frontend (nothing bad, as it happens), which was a little disconcerting. But, at the time, it hadn’t yet been released (this happened on the 9th Feb). I shall likely be giving it ago at some point.&lt;/p&gt;

&lt;p&gt;Other than sessions themselves, I met up with &lt;a href=&quot;https://twitter.com/bob_moss&quot;&gt;Bob&lt;/a&gt; on the Friday for an eventful, but good evening at the &lt;a href=&quot;http://fosdem.org/2012/beerevent&quot;&gt;FOSDEM Beer Event&lt;/a&gt;. There, I also met Kostas Georgiou &amp;amp; Elaine McLeod from &lt;a href=&quot;http://opengamma.com/&quot;&gt;OpenGamma&lt;/a&gt;, who are working on an open platform for the financial services industry. Interesting stuff.&lt;/p&gt;

&lt;p&gt;On Sunday afternoon, I met up with &lt;a href=&quot;http://www.flickr.com/photos/tesfruitsmonjus/&quot;&gt;Goedele&lt;/a&gt;, which was good fun. After grabbing some lunch, we went to this odd bar which serves beer in skulls. The music was good, too.&lt;/p&gt;

&lt;p&gt;Belgium was exceptionally cold for much of the time. When Ben and I arrived, it’d already been snowing for quite a while. I gather on the Friday night it got down to somewhere around -12°.&lt;/p&gt;

&lt;p&gt;Belgium is a lovely country, which topped off an excellent weekend.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>PAM for OmniAuth: omniauth-pam</title>
        <link href="https://nickcharlton.net/posts/pam-for-omniauth.html" />
        <id>https://nickcharlton.net/posts/pam-for-omniauth.html</id>
        <published>Sun, 15 Jan 2012 00:00:00 +0000</published>
        <updated>Sun, 15 Jan 2012 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I have a couple of small web applications that I have built for myself (wiki, system monitoring, etc.) There didn’t seem much point in adding a database for authentication, so I put together a strategy for using &lt;a href=&quot;http://en.wikipedia.org/wiki/Linux_PAM&quot;&gt;PAM&lt;/a&gt; and &lt;a href=&quot;https://github.com/intridea/omniauth/&quot;&gt;OmniAuth&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It depends on OmniAuth (~&amp;gt; 1.0), &lt;a href=&quot;https://github.com/canweriotnow/rpam-ruby19&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rpam-ruby19&lt;/code&gt;&lt;/a&gt; and the PAM headers (that’s the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libpam0g-dev&lt;/code&gt; package on &lt;a href=&quot;http://packages.debian.org/squeeze/libpam0g-dev&quot;&gt;Debian&lt;/a&gt; and Ubuntu.)&lt;/p&gt;

&lt;p&gt;It has only been tested on Debian 6.0 using Ruby 1.9.3-p0 (but there’s no reason why it won’t work elsewhere.)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://github.com/nickcharlton/omniauth-pam&quot;&gt;The project is on GitHub&lt;/a&gt;. Log an issue if something doesn’t work as you expect.&lt;/p&gt;

&lt;h3 id=&quot;usage&quot;&gt;Usage&lt;/h3&gt;

&lt;p&gt;Include provider type:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;use Rack::Session::Cookie
use OmniAuth::Strategies::PAM
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Implement the callback (as in the OmniAuth documentation), and then navigate to: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/auth/pam&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It uses the authenticated user as the UID, as on a Linux system this would be unique.&lt;/p&gt;

&lt;h3 id=&quot;supporting-ruby-18&quot;&gt;Supporting Ruby 1.8&lt;/h3&gt;

&lt;p&gt;There is an older gem available for Ruby 1.8 for supporting PAM. The syntax is slightly different, but only a small change if you wanted it.&lt;/p&gt;

&lt;p&gt;Instead of including &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rpam-ruby19&lt;/code&gt; instead use &lt;a href=&quot;http://rpam.rubyforge.org/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rpam&lt;/code&gt;&lt;/a&gt; and change the implementation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;callback_response&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lib/omniauth/strategies/pam.rb&lt;/code&gt; to:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;def callback_phase
    unless authpam(request[&apos;username&apos;], request[&apos;password&apos;])
        return fail!(:invalid_credentials)
    end

    super
end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You will also need to add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;include Rpam&lt;/code&gt; beneath &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;include OmniAuth::Strategy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As it’s only small (the whole thing is tiny as it is) I figured it’d be best to document the difference, rather than aim to support two different gems.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Configuring Apache &amp;amp; PHP on Lion</title>
        <link href="https://nickcharlton.net/posts/configuring-apache-and-php-on-lion.html" />
        <id>https://nickcharlton.net/posts/configuring-apache-and-php-on-lion.html</id>
        <published>Tue, 10 Jan 2012 00:00:00 +0000</published>
        <updated>Tue, 10 Jan 2012 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;There are too many terrible articles on configuring Apache and PHP on the Mac, especially for Lion. Even worse are the suggestions of using other versions, or overly complex configuration methods.&lt;/p&gt;

&lt;p&gt;Apache &amp;amp; PHP are included with Lion by default, but various parts are disabled. This will show you how to enable them without breaking the local install.&lt;/p&gt;

&lt;h3 id=&quot;configuration-files&quot;&gt;Configuration Files&lt;/h3&gt;

&lt;p&gt;The Apache config files are located in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/apache2&lt;/code&gt; (the standard place.)&lt;/p&gt;

&lt;p&gt;The most important part of this directory is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;httpd.conf&lt;/code&gt;. This is the main configuration file. In the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;users/&lt;/code&gt; subdirectory are the configurations for local users (accessible through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost/~&amp;lt;username&amp;gt;&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;The PHP configuration file (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;php5.conf&lt;/code&gt;) is held under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;other/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The global virtual-hosts config file is held under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extra/httpd-vhosts.conf&lt;/code&gt;. Although, by default this is commented out in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;httpd.conf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The rest is mostly Apple specifics, including some of the tools included with Lion Server, and other areas of the config you are unlikely to need to change.&lt;/p&gt;

&lt;h3 id=&quot;permissions&quot;&gt;Permissions&lt;/h3&gt;

&lt;p&gt;By default, the main &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;httpd.conf&lt;/code&gt; file is set to only be readable by all (444.) I would assume this is so users do not inadvertantly break the defaults.&lt;/p&gt;

&lt;p&gt;To change it to be writeable by it’s own user (root), change it to 644 (readable by all, readable by it’s owner.) like so:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;chmod 644 httpd.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can then edit it using sudo, in your favourite editor.&lt;/p&gt;

&lt;h3 id=&quot;enabling-php&quot;&gt;Enabling PHP&lt;/h3&gt;

&lt;p&gt;Apple have always shipped with PHP disabled by default (even in Lion Server, you need to select a checkbox to specifically enable it.)&lt;/p&gt;

&lt;p&gt;In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;httpd.conf&lt;/code&gt;, it is located somewhere around line 111, towards the end of the other &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LoadModule&lt;/code&gt; statements. This line is commented out. You need to remove the hash to it looks something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;LoadModule alias_module libexec/apache2/mod_alias.so
LoadModule rewrite_module libexec/apache2/mod_rewrite.so
LoadModule php5_module libexec/apache2/libphp5.so                                    
 
#Apple specific modules
LoadModule apple_userdir_module libexec/apache2/mod_userdir_apple.so
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After doing this, you will need to restart Apache. You can do that from System Preferences/Sharing, or like so:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo apachectl restart
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;virtual-hosts&quot;&gt;Virtual Hosts&lt;/h3&gt;

&lt;p&gt;For local development (for PHP this isn’t so often), I usually add a virtual host for the project I’m working on, and then adjust &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/hosts&lt;/code&gt; to give it a usable domain.&lt;/p&gt;

&lt;p&gt;You’ll need to edit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;httpd.conf&lt;/code&gt; again. There is an include for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;httpd-vhosts.conf&lt;/code&gt; quite a way in, somewhere around line 623.&lt;/p&gt;

&lt;p&gt;You’ll find you’ll want to remove most of the default example content from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extras/httpd-vhost.conf&lt;/code&gt; (by default accessing anything will give you a 403: Forbidden error.)&lt;/p&gt;

&lt;p&gt;From there, each project/application/etc will need a VirtualHost block configured for it. This allows Apache to respond to a given domain.&lt;/p&gt;

&lt;p&gt;The logging entries inside the block are optional, but recommended. Console.app is useful to keep an eye on the logs (it will automatically refresh when it changes.)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;VirtualHost *:80&amp;gt;
	DocumentRoot /path/to/files
	ServerName project.example.com
	
	ErrorLog /path/to/files/logs/error.log
	LogLevel warn
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To get the domain working, you need to edit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/hosts&lt;/code&gt; and add a line something like this (below the comments, before the rest.):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;127.0.0.1	project.example.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, you should be able to navigate to that domain and access it.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This is probably the most elegant way of running PHP applications locally. It keeps the already present tools, but makes them work as expected - which is far nicer than hacking other tools and configurations in place.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Christmas Books</title>
        <link href="https://nickcharlton.net/posts/christmas-books.html" />
        <id>https://nickcharlton.net/posts/christmas-books.html</id>
        <published>Fri, 06 Jan 2012 00:00:00 +0000</published>
        <updated>Fri, 06 Jan 2012 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I read quite a few things over Christmas, here’s some notes on a few of them:&lt;/p&gt;

&lt;h3 id=&quot;mobile-first--emotional-design&quot;&gt;&lt;a href=&quot;http://www.abookapart.com/products/mobile-first&quot;&gt;Mobile First&lt;/a&gt; &amp;amp; &lt;a href=&quot;http://www.abookapart.com/products/designing-for-emotion&quot;&gt;Emotional Design&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;These were probably the shortest two that I read, but both jam packed with some quite useful information. With Mobile First, some of it was already preaching to the converted, but the examples and approaches were useful to see.&lt;/p&gt;

&lt;p&gt;Emotional Design, however, made me think. It’s about optimising designs for the way human emotions operate. And, it works very well.&lt;/p&gt;

&lt;p&gt;Much of the book outlines case studies, some from Aarron’s work with MailChimp, and others from elsewhere. It does a very good job of making its case - something that I’ll think about in the future.&lt;/p&gt;

&lt;h3 id=&quot;a-phd-is-not-enough&quot;&gt;&lt;a href=&quot;http://www.amazon.co.uk/gp/product/0465022227/ref=as_li_ss_tl?ie=UTF8&amp;amp;tag=nisbl-21&amp;amp;linkCode=as2&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=0465022227&quot;&gt;A PhD is Not Enough&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;I read this after spending a good chunk of time reading &lt;a href=&quot;http://matt.might.net/&quot;&gt;Matt Might&lt;/a&gt;’s blog. It’s a book about academic careers and attempts to reveal exactly what that entails.&lt;/p&gt;

&lt;p&gt;Like most books on the same subject, it’s US centric, but still valuable. If the thought of academic is on the horizon, I’d suggest you give it a read.&lt;/p&gt;

&lt;h3 id=&quot;the-passionate-programmer&quot;&gt;&lt;a href=&quot;http://www.amazon.co.uk/gp/product/1934356344/ref=as_li_ss_tl?ie=UTF8&amp;amp;tag=nisbl-21&amp;amp;linkCode=as2&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=1934356344&quot;&gt;The Passionate Programmer&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;This is about programming careers. I think it’s well targeted at those soon to be leaving University or in University studying something programming related.&lt;/p&gt;

&lt;p&gt;I find what most people say on careers to be simply offensive, this is good.&lt;/p&gt;

&lt;p&gt;A big pain point I see in people is seeing what they’re being taught at University to be the be-all and end-all of what they need to know to get a job. For some, this is obvious, but for others not so. The book makes a good point of suggesting that you firstly should be problem, not tool orientated, and that also you should be pushing out into other technologies. Especially new ones.&lt;/p&gt;

&lt;p&gt;For the former, this means pushing far out of your comfort zone. Used to Java? Learn Ruby, or Objective-C. Or, even better go functional and learn something like Haskell. But, more important is to remember what you’re working on now is just the thing of the moment. If you keep that view, you can keep ploughing forward. And anyway, if you learn around your favourite technology and methodologies enough, you are still improving on the original one.&lt;/p&gt;

&lt;p&gt;It is a little dry at times, and somewhat fear driven (there’s lots about the risk of having your job outsourced), but overall it’s a good book. It has some nice personal case studies, too.&lt;/p&gt;

&lt;h3 id=&quot;the-macintosh-way&quot;&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/The_Macintosh_Way&quot;&gt;The Macintosh Way&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;This wasn’t what I expected. It’s a book about applying the way the Macintosh was created to other companies. Some things are a little dated (it was published in 1990.)&lt;/p&gt;

&lt;p&gt;It doesn’t contain a gushing history, but a a slightly rose tinted, but critical look at how the Macintosh was crafted (and maintained after Jobs left.)&lt;/p&gt;

&lt;p&gt;The point of the book is how to apply this to other companies. Beware of the dating chapter. It’s painful.&lt;/p&gt;

&lt;h3 id=&quot;core-data-for-ios&quot;&gt;&lt;a href=&quot;http://www.amazon.co.uk/gp/product/0321670426/ref=as_li_ss_tl?ie=UTF8&amp;amp;tag=nisbl-21&amp;amp;linkCode=as2&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=0321670426&quot;&gt;Core Data for iOS&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;This has got to be the first book that has made me enjoy dealing with data. Up until this point, I had avoided Core Data (it is a complex framework.) But, I got this as a Christmas Present.&lt;/p&gt;

&lt;p&gt;For iOS, Core Data is essentially a wrapper and set of tools for dealing with SQLite. I’ve not finished reading it yet, but so far this has given a enjoyable discussion of the way Core Data works and how to go about using it. It has some nice tips on performance, too - especially important if you are handling large data sets.&lt;/p&gt;

&lt;p&gt;The information is not limited to just iOS, either. Whilst this is the primary focus, it does cover enough if you are looking into the Mac, too.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;I did read a lot of other things, too (mostly catching up.) You can find those &lt;a href=&quot;http://pinboard.in/u:nickcharlton&quot;&gt;over on Pinboard&lt;/a&gt;.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Postgres on Lion</title>
        <link href="https://nickcharlton.net/posts/postgres-on-lion.html" />
        <id>https://nickcharlton.net/posts/postgres-on-lion.html</id>
        <published>Mon, 28 Nov 2011 00:00:00 +0000</published>
        <updated>Mon, 28 Nov 2011 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I’ve recently been picking up &lt;a href=&quot;http://www.postgresql.org/&quot;&gt;PostgreSQL&lt;/a&gt;. It’s very nice. Unfortunately, I had a few issues with it and Lion. Lion Server now uses Postgres as its default database (replacing MySQL), and consequently it looks like Apple included a bunch of tools in standard Lion, too.&lt;/p&gt;

&lt;p&gt;Unfortunately, these clash a if you attempt to install and use the database daemon.&lt;/p&gt;

&lt;h2 id=&quot;install--configure&quot;&gt;Install &amp;amp; Configure&lt;/h2&gt;

&lt;p&gt;To start with, install Postgres from &lt;a href=&quot;http://mxcl.github.com/homebrew/&quot;&gt;Homebrew&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;brew install postgres
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then (as told) initialise the database:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;initdb /usr/local/var/postgres
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But ignore the rest (as in, don’t add the LaunchAgent).&lt;/p&gt;

&lt;p&gt;Next, it is necessary to adjust your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$PATH&lt;/code&gt; slightly. On OS X, this is stored in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/paths&lt;/code&gt;. You need to push &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/local/bin&lt;/code&gt; to be searched before &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/bin&lt;/code&gt;. This will ensure that the homebrew compiled binaries are used instead of the system ones.&lt;/p&gt;

&lt;h2 id=&quot;control&quot;&gt;Control&lt;/h2&gt;

&lt;p&gt;The LaunchAgent step that was skipped is so that you can control Postgres yourself. If this is setup, it will interfere with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pg_ctl&lt;/code&gt; command. Usually, the LaunchAgent would be used to manage it all for you.&lt;/p&gt;

&lt;h3 id=&quot;startingstopping-postgres&quot;&gt;Starting/Stopping Postgres&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pg_ctl -D /usr/local/var/postgres -l /usr/local/var/postgres/server.log start

pg_ctl -D /usr/local/var/postgres stop -s -m fast
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;These commands will start and stop Postgres, respectively. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-m fast&lt;/code&gt; flag on the stop command ignores the presence of other clients as it goes down.&lt;/p&gt;

&lt;h3 id=&quot;aliases&quot;&gt;Aliases&lt;/h3&gt;

&lt;p&gt;As these are a little long, I have these aliased to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pgstart&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pgstop&lt;/code&gt;. &lt;a href=&quot;https://github.com/nickcharlton/dotfiles/blob/master/_bash_aliases&quot;&gt;Done like this&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;export PGDATA=&apos;/usr/local/var/postgres&apos;
alias pgstart=&apos;pg_ctl -l $PGDATA/server.log start&apos;
alias pgstop=&apos;pg_ctl stop -m fast&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;So, there you go. That’s how to setup Postgres properly on Lion. My next challenge is to make libpq (the C library for Postgres) up and running.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Drawing Primitives with Quartz</title>
        <link href="https://nickcharlton.net/posts/drawing-primitives-with-quartz.html" />
        <id>https://nickcharlton.net/posts/drawing-primitives-with-quartz.html</id>
        <published>Sat, 26 Nov 2011 00:00:00 +0000</published>
        <updated>Sat, 26 Nov 2011 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;I’ve always found drawing graphics complicated. Not that because maths is involved (that’s the good bit), but finding simple examples to work out what the hell is going on. Usually, the calls required are a little different, and required to ensure something actually gets drawn — Quartz is no different. This intends to serve as a few notes on using the basics of QuartzCore, the drawing tools present in iOS and on Mac OS X.&lt;/p&gt;

&lt;p&gt;Most of the calls are just C, but the example (linked at the bottom) shows all of the examples drawn inside a UIView on iOS. The rest of this assume you know what I have just written, and what &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CGRectMake()&lt;/code&gt; does.&lt;/p&gt;

&lt;h2 id=&quot;requirements&quot;&gt;Requirements&lt;/h2&gt;

&lt;p&gt;You need to add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QuartzCore&lt;/code&gt; framework to your project in Xcode. Then you need to import the headers in relevant files. All that is required for the examples here is: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#import &amp;lt;QuartzCore/QuartzCore.h&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;a-bit-of-theory&quot;&gt;A Bit of Theory&lt;/h2&gt;

&lt;p&gt;Quartz works similar to a pen on paper, especially for lines and rectangles. Much like picking the right type of pen before making marks on paper, Quartz works in a similar way. Changing the attributes of lines (the pen) is done just before drawing. Drawings also layer in a similar manner. If you tell Quartz to draw over a point that you have already drawn on, it will be layered on top of it.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// get the initial context
CGContextRef context = UIGraphicsGetCurrentContext();

// save the current state, as we&apos;ll overwrite this
CGContextSaveGState(context);

// draw stuff

// do the actual drawing
CGContextStrokePath(context);

// restore the state back after drawing on it.
CGContextRestoreGState(context);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The rest of these examples assume the above. This gets the current drawing context (in this case, the UIView we are going to draw onto, but this could be almost anything, including a PDF). It saves the current state, as this is what we’ll be accessing.&lt;/p&gt;

&lt;p&gt;After the drawing statements, we proceed to do the actual drawing, and then save it back to the context we were working with. This then renders the drawing on the relevant view.&lt;/p&gt;

&lt;h2 id=&quot;lines&quot;&gt;Lines&lt;/h2&gt;

&lt;p&gt;The simplest primitive to draw is a line. These (and arcs) make up the majority of work done with Quartz. It also fits the best within the real-world drawing metaphor.&lt;/p&gt;

&lt;p&gt;First, you need to position the pen, then you can draw a line to the next coordinate:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// move the pen to the starting point
CGContextMoveToPoint(context, 10, 10);

// draw a line to another point
CGContextAddLineToPoint(context, 290, 10);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This starts at position 10, 10 and draws a line parallel to the top of the view to the other side. It looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://nickcharlton.net/resources/drawing-primitives/line.png&quot; alt=&quot;A line drawn on a UIView&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The pixels are relevant to the view they are created in.&lt;/strong&gt; So, drawing to 100, 100 would draw a line at a 45° angle pointing down towards the middle of the view.&lt;/p&gt;

&lt;h2 id=&quot;styling&quot;&gt;Styling&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;By default, a line is 1px thick. You can change this by setting the second parameter of: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CGContextSetLineWidth(context, 5)&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;To set the colour: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CGContextSetRGBStrokeColor(context, 1, 0, 0, 1);&lt;/code&gt;. Where the numbers are RGBA values.&lt;/li&gt;
  &lt;li&gt;By default, the stroke straddles both sides of the line. If you’re positioning elements with a 5px stroke, you will want to offset the position by 2.5px.&lt;/li&gt;
  &lt;li&gt;Each call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CGContextStrokePath(context);&lt;/code&gt; commits the drawing to the canvas. If you want to set different styling for different elements, you need to commit it first. Then ‘pick up the pen’ with the next element.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;rectangles&quot;&gt;Rectangles&lt;/h2&gt;

&lt;p&gt;For shapes, the position is based upon a CGRect. This makes it handy for colouring the position of elements if you are drawing/comparing more complex shapes. This also means that it doesn’t use the notion of a pen.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CGContextAddRect(context, CGRectMake(10, 20, 280, 30));
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This would draw a rectangle just below the line, 30px high. Rectangles are rendered from their origin point (top left corner) and drawn out from there.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://nickcharlton.net/resources/drawing-primitives/box.png&quot; alt=&quot;A box drawn on a UIView&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;circles&quot;&gt;Circles&lt;/h2&gt;

&lt;p&gt;Circles (also called arcs and ellipses) are more complicated. There are two ways to define a complete circle, and various other ways to define different types of arc — including Bézier curves. The snippet below just describes circles, however:&lt;/p&gt;

&lt;p&gt;The simplest way to define a circle is placing one inside a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CGRect&lt;/code&gt;. The circle will be drawn to fit the best it can inside the rectangle.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CGContextAddEllipseInRect(context, CGRectMake(50, 70, 200, 200));
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will draw a circle with 200px in diameter below the other elements.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://nickcharlton.net/resources/drawing-primitives/circle.png&quot; alt=&quot;A circle drawn on a UIView&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;memory-management&quot;&gt;Memory Management&lt;/h2&gt;

&lt;p&gt;Assuming you are not using ARC, the general rules apply from Core Foundation:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If you call a function that has ‘Create’ or ‘Copy’ in the name, it’s yours.&lt;/li&gt;
  &lt;li&gt;If you get an object from elsewhere you don’t own it, unless you explicitly retain it.&lt;/li&gt;
  &lt;li&gt;And to release you should call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CFRelease&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;example-project&quot;&gt;Example Project&lt;/h2&gt;

&lt;p&gt;You can &lt;a href=&quot;https://nickcharlton.net/resources/drawing-primitives/project.zip&quot;&gt;download an example project here&lt;/a&gt;. It contains the examples above, but put all into a single view. The object of importance is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PrimitivesView&lt;/code&gt;. This provides the drawing, the rest is just a simple FlipSideViewController based project using Storyboards and ARC (for ease.) The &lt;a href=&quot;http://github.com/nickcharlton/DrawingPrimitives&quot;&gt;repository is also on GitHub&lt;/a&gt;.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>The Social Graph &amp;amp; Thoughts on Identity</title>
        <link href="https://nickcharlton.net/posts/the-social-graph-identity.html" />
        <id>https://nickcharlton.net/posts/the-social-graph-identity.html</id>
        <published>Wed, 09 Nov 2011 00:00:00 +0000</published>
        <updated>Wed, 09 Nov 2011 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://blog.pinboard.in/2011/11/the_social_graph_is_neither/&quot;&gt;Maciej Ceglowski on “The Social Graph is Neither”&lt;/a&gt;…&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Asking computer nerds to design social software is a little bit like 
hiring a Mormon bartender. Our industry abounds in people for whom 
social interaction has always been more of a puzzle to be reverse-engineered 
than a good time to be had, and the result is these vaguely Martian protocols.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Attempting to apply a graph to people works in the first instance. But, whilst we are all connected, we are connected to each other in mind bogglingly different ways. And, people care about that.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Social networks exist to sell you crap. The icky feeling you get when your 
friend starts to talk to you about Amway, or when you spot someone passing 
out business cards at a birthday party, is the entire driving force behind 
a site like Facebook.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Now tell me one bit of original culture that’s ever come out of Facebook.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Facebook and Google+ are passing phases. Hopefully the end of this phase will hurry up. Pictures of kittens might get tedious, but I can’t imagine a web without them. And anyway, &lt;a href=&quot;http://zachholman.com/posts/shit-work/&quot;&gt;“Liking” is “shit work”.&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;To me, a more important problem to solve is that of identity. A lot of &lt;a href=&quot;http://factoryjoe.com/blog/&quot;&gt;smart&lt;/a&gt; &lt;a href=&quot;http://davidrecordon.com/&quot;&gt;people&lt;/a&gt; were working on things like &lt;a href=&quot;http://openid.net&quot;&gt;OpenID&lt;/a&gt; (and then they went to work for Google &amp;amp; Facebook. &lt;em&gt;sigh&lt;/em&gt;.) Whilst I think OpenID had its flaws, every design can be iterated on. The growth of “Sign in with [Twitter/Facebook/Google/etc.]” makes me sad.&lt;/p&gt;

&lt;p&gt;I talked about this briefly at the weekend with &lt;a href=&quot;http://bma.li&quot;&gt;Ben&lt;/a&gt;. We didn’t come to much of a solution, other than thinking that something like ssh-agent might work.&lt;/p&gt;

&lt;p&gt;As humans, we are too ready and willing to give up the one thing we truly defines us: Our Identity.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Introducing UrbanScraper, and an Alfred Extension</title>
        <link href="https://nickcharlton.net/posts/introducing-urbanscraper.html" />
        <id>https://nickcharlton.net/posts/introducing-urbanscraper.html</id>
        <published>Fri, 28 Oct 2011 00:00:00 +0000</published>
        <updated>Fri, 28 Oct 2011 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Over the last two evenings, I’ve been working on a little toy. Well, two. The first one is a web service for getting definitions from &lt;a href=&quot;http://urbandictionary.com&quot;&gt;Urban Dictionary&lt;/a&gt; and the other is an Alfred Extension to allow you to get those definitions using your keyboard.&lt;/p&gt;

&lt;h2 id=&quot;urbanscraper&quot;&gt;&lt;a href=&quot;http://urbanscraper.herokuapp.com/&quot;&gt;UrbanScraper&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;It lead from a conversation with someone where I ended up having to reference some stuff a few times, I have a terrible memory for abbreviations and acronyms. I wanted to be able to find the definitions using Alfred, but Urban Dictionary didn’t have an API.&lt;/p&gt;

&lt;p&gt;Apparently, there used to be an API (albeit, SOAP), and there appeared to be other stuff around, but I figured I’d take a hack at it myself.&lt;/p&gt;

&lt;p&gt;UrbanScraper scrapes the definitions (on demand) from Urban Dictionary and outputs simple JSON for you to use. It mostly uses XPath.&lt;/p&gt;

&lt;p&gt;It’s not exactly speedy (XPath isn’t terribly fast), but it does the job with very little code. I’ve put it up on &lt;a href=&quot;http://github.com/nickcharlton/urbanscraper&quot;&gt;GitHub&lt;/a&gt;. For the time being, it’s hosted up on Heroku.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;For the love of god, please don’t use this as an example of how to build Sinatra/Ruby apps, how to do Screen Scraping, or how to build RESTful Web Services. It was hacked together pretty quickly.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-alfred-extension&quot;&gt;The Alfred Extension&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/resources/urban_dictionary.alfredextension&quot;&gt;You can download the extension here&lt;/a&gt;. Obviously, you’ll need &lt;a href=&quot;http://alfredapp.com&quot;&gt;Alfred&lt;/a&gt; and the &lt;a href=&quot;http://www.alfredapp.com/powerpack/&quot;&gt;Powerpack&lt;/a&gt; to use it. But damn, is it worth it.&lt;/p&gt;

&lt;p&gt;It’s setup with the keyword &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;urban&lt;/code&gt;. Passing something like “zomg” will give you a Growl notification containing: “zOMG is a variant of the all-to-popular acronym “OMG”, meaning “Oh My God”. The “z” was originally a mistake while attempting to hit the shift…”.&lt;/p&gt;

&lt;p&gt;It (like UrbanScraper), just grabs the first result. So it might not always be very good.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/about&quot;&gt;Shout&lt;/a&gt; if anything breaks.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Jacks: A place to start web projects</title>
        <link href="https://nickcharlton.net/posts/jacks-a-place-to-start-web-projects.html" />
        <id>https://nickcharlton.net/posts/jacks-a-place-to-start-web-projects.html</id>
        <published>Sat, 22 Oct 2011 00:00:00 +0000</published>
        <updated>Sat, 22 Oct 2011 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;A couple of weeks ago, I started a project which intended to pull together all of the stuff that I had learnt about responsive design. I also wanted to have a generic starting point for web projects. I came up with Jacks, a simple web “framework” which does exactly that.&lt;/p&gt;

&lt;h3 id=&quot;being-responsive&quot;&gt;Being Responsive&lt;/h3&gt;

&lt;p&gt;Responsive Design is all about providing one singular web experience that is optimal for many different devices. With the growth of what some call the “mobile web”, you’ve probably come across some horrible web experiences.&lt;/p&gt;

&lt;p&gt;I believe that we should present an optimal experience for as many browsers, in as many situations, as possible.&lt;/p&gt;

&lt;p&gt;Jacks uses a fluid grid which is tamed with the use of media queries. It’s very much based around the work done by &lt;a href=&quot;http://unstoppablerobotninja.com/&quot;&gt;Ethan Marcotte&lt;/a&gt;, in his book, “&lt;a href=&quot;http://www.abookapart.com/products/responsive-web-design&quot;&gt;Responsive Web Design&lt;/a&gt;”. If you’re interested in this at all, I urge you to go ahead and read it. It does a much better explanation than I could ever aim to do.&lt;/p&gt;

&lt;h3 id=&quot;jacks-gives&quot;&gt;Jacks Gives&lt;/h3&gt;

&lt;p&gt;So, what does this actually do? Jacks provides you a bunch of presets to work off of.&lt;/p&gt;

&lt;p&gt;These presets include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Some default libraries:
    &lt;ul&gt;
      &lt;li&gt;Eric Meyer’s CSS Reset&lt;/li&gt;
      &lt;li&gt;jQuery&lt;/li&gt;
      &lt;li&gt;LESS CSS&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;An example HTML page to start off with.&lt;/li&gt;
  &lt;li&gt;Some basic CSS styles for:
    &lt;ul&gt;
      &lt;li&gt;Typography&lt;/li&gt;
      &lt;li&gt;Colours&lt;/li&gt;
      &lt;li&gt;Common Effects (shadows, rounded corners, etc)&lt;/li&gt;
      &lt;li&gt;Flexible Media Blocks (aka, max-width: 100%)&lt;/li&gt;
      &lt;li&gt;Media Queries for common display sizes.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It uses &lt;a href=&quot;http://lesscss.org/&quot;&gt;LESS&lt;/a&gt; to make CSS easier and more maintainable.&lt;/p&gt;

&lt;p&gt;Jacks uses media queries to tame the fluid grid. The default layout is assumed to be approximately 960px (which you’ve probably already been doing), then it provides increasingly smaller steps until a much larger block for handling large browser windows (&amp;gt;1200px).&lt;/p&gt;

&lt;p&gt;I call this the upside-down triangle on a shelf method. I find it works quite well.&lt;/p&gt;

&lt;h3 id=&quot;github&quot;&gt;&lt;a href=&quot;http://github.com/nickcharlton/jacks&quot;&gt;GitHub&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;Like most of my projects, &lt;a href=&quot;http://github.com/nickcharlton/jacks&quot;&gt;it’s up on GitHub&lt;/a&gt;. It’s licensed under the MIT license. I intend to improve and adjust this as I go.&lt;/p&gt;

&lt;p&gt;I encourage you to fork and add your own changes. Do a pull request if you want it to be pushed up into the main repository.&lt;/p&gt;

&lt;p&gt;It’s been used in one production project so far. I’d certainly be interested in hearing if it’s used elsewhere!&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Configuring Gitosis on Debian</title>
        <link href="https://nickcharlton.net/posts/configuring-gitosis-on-debian.html" />
        <id>https://nickcharlton.net/posts/configuring-gitosis-on-debian.html</id>
        <published>Wed, 21 Sep 2011 00:00:00 +0000</published>
        <updated>Wed, 21 Sep 2011 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Gitosis is a tool for easing the hosting of Git repositories. For pushing to remote [servers], Git uses SSH. Which is great. But, what if you don’t want those users to have shell accounts, and you want to be able to control who has access to repositories? Well, that’s what Gitosis does.&lt;/p&gt;

&lt;p&gt;Gitosis is in Debian’s package manager, but I wasn’t too keen on the Debian provided configuration, so here’s a few steps to sanitise it:&lt;/p&gt;

&lt;p&gt;First, install it. You’ll obviously need the dependencies.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo apt-get install gitosis
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, we’ll change a few settings. You might find the documentation useful for this, you’ll find that under: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/share/doc/gitosis&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Gitosis stores is config in a Git repository. This also means that it doesn’t have a configuration until you initialise it. Now, I’d rather use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; as the user rather than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gitosis&lt;/code&gt;. So first, create a new user:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo adduser \
    --system \
    --shell /bin/sh \
    --gecos &apos;Git&apos; \
    --group \
    --disabled-password \
    --home /home/git \
    git
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can also remove the gitosis user: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo userdel gitosis&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, you’ll need your public key somewhere. Then, setup the admin repository:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo -H -u git gitosis-init &amp;lt; &amp;lt;path to your public key&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, on your local machine, you can pull down the newly initialised Gitosis admin repository.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ git clone git@server:gitosis-admin.git
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gitosis-admin&lt;/code&gt; directory contains the main config file (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gitosis.conf&lt;/code&gt;) and the public keys of all of the users able to access repositories.&lt;/p&gt;

&lt;p&gt;A few notes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The members line in the config file needs to match up with a user in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keydir&lt;/code&gt;. But without the .pub extension.&lt;/li&gt;
  &lt;li&gt;Groups are used to split up users and/or projects. You define a group, which holds a repository and the users with that permission.&lt;/li&gt;
  &lt;li&gt;You can set read/write or read only privileges on groups. But, you’ll need to create a new group to define the different privileges for different users.&lt;/li&gt;
  &lt;li&gt;Members are separated by spaces.&lt;/li&gt;
  &lt;li&gt;A group can also be used to define members, which then can be assigned to another group. (You access the members as a variable by prefixing @).&lt;/li&gt;
  &lt;li&gt;When you define a repository in Gitosis, it will be created on the server when you push it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll find &lt;a href=&quot;http://progit.org/book/ch4-7.html&quot;&gt;Pro Git probably gives you a better explanation of some of the other features&lt;/a&gt;. But you should (after this) be able to setup Gitosis on Debian quite nicely.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>I forked QuickDialog</title>
        <link href="https://nickcharlton.net/posts/i-forked-quickdialog.html" />
        <id>https://nickcharlton.net/posts/i-forked-quickdialog.html</id>
        <published>Mon, 19 Sep 2011 00:00:00 +0000</published>
        <updated>Mon, 19 Sep 2011 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;A couple of weeks ago, &lt;a href=&quot;http://escoz.com/&quot;&gt;Eduardo Scoz (ESCOZ, Inc)&lt;/a&gt; released &lt;a href=&quot;http://escoz.com/quickdialog-released/&quot;&gt;QuickDialog&lt;/a&gt;, a rather nice way of producing UITableView based dialog controls on iOS.&lt;/p&gt;

&lt;p&gt;In my own projects, and some of those I’ve been working on &lt;a href=&quot;http://nickcharlton.net/post/starting-at-rokk-media&quot;&gt;at work&lt;/a&gt;, I’ve wanted to be able to use QuickDialog to make development faster. Unfortunately, it requires Automatic Reference Counting, and I usually work on the current stable version of iOS.&lt;/p&gt;

&lt;p&gt;Whilst I do think ARC is a good thing, I wanted to use QuickDialog now. So, I forked it and adjusted it where needed to make it compile under iOS 4.3 and the Xcode 4.1 toolchain without errors.&lt;/p&gt;

&lt;p&gt;It’s not completely tested and I don’t intend to closely track the original branch, nor actively maintain it once iOS 5 is released. But, it’s there and working if you want it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: In the future, I intend to push changes back into the original QuickDialog (especially when I find I want different controls.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/nickcharlton/QuickDialog/&quot;&gt;You can get it over on GitHub&lt;/a&gt;.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Where&apos;s Next? 1.0.1 Release</title>
        <link href="https://nickcharlton.net/posts/wheres-next-101-release.html" />
        <id>https://nickcharlton.net/posts/wheres-next-101-release.html</id>
        <published>Thu, 18 Aug 2011 00:00:00 +0000</published>
        <updated>Thu, 18 Aug 2011 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;After a bit of an issue with the original launch, &lt;em&gt;Where’s Next?&lt;/em&gt; 1.0.1 has now been approved and is &lt;a href=&quot;http://itunes.apple.com/gb/app/wheres-next/id454450198?mt=8&quot;&gt;in the App Store&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This fixes the crashing on load bug which ended up falling through testing (if you saw it, you’d have thought I didn’t test it, but I did, but obviously not well enough).&lt;/p&gt;

&lt;p&gt;For the those who are interested, it was a pointer to integer comparison that caused the problem. Oddly, it was not caught by GCC/LLVM. Unfortunately, that’s one of the disadvantages of an unmanaged memory model.&lt;/p&gt;

&lt;p&gt;But anyway. I’m pleased it is finally in the store.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Where&apos;s Next? Now In the App Store.</title>
        <link href="https://nickcharlton.net/posts/wheres-next-now-in-the-app-store.html" />
        <id>https://nickcharlton.net/posts/wheres-next-now-in-the-app-store.html</id>
        <published>Fri, 12 Aug 2011 00:00:00 +0000</published>
        <updated>Fri, 12 Aug 2011 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;After many, many months of development, my first iOS project is now in the App Store. It’s a utility app for sorting places into the most efficient way to get there, from the site:&lt;/p&gt;

&lt;blockquote&gt;&lt;em&gt;Where&apos;s Next?&lt;/em&gt; takes the places you need to visit and according to where you are now tells you the quickest way to visit them.&lt;/blockquote&gt;

&lt;p&gt;I’ve spent the last month or two slowly polishing off and fixing bugs on the project I started before the start of this year with the intent of learning iOS development.&lt;/p&gt;

&lt;p&gt;I could have released (and “finished”) it a long time ago, but being a perfectionist, it had to be right before I submitted it. With it’s limited feature set, I now think it is ready for release. It’s said that you should be embarrassed with your 1.0 and I am. It only implements the core idea, but this means that there are many possibilities for the future.&lt;/p&gt;

&lt;p&gt;With all projects, this wouldn’t have happened without those who I leaned on:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Without &lt;a href=&quot;http://www.plymouth.ac.uk/staff/noutram&quot;&gt;Dr Nick Outram&lt;/a&gt;’s iPhone Programming Course, I’d probably still be trying to master Cocoa and not releasing my own apps and &lt;a href=&quot;http://nickcharlton.net/post/starting-at-rokk-media&quot;&gt;having a job doing it&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;The icon was produced by &lt;a href=&quot;http://ulrikstoch.com/&quot;&gt;Ulrik Stoch Jensen&lt;/a&gt;, a young icon designer from Denmark who I found through Dribbble. He’s fantastic to work with and even put up with my backseat designing.&lt;/li&gt;
  &lt;li&gt;Those who helped test: &lt;a href=&quot;http://thisisthechris.co.uk/&quot;&gt;Chris Hunt&lt;/a&gt;, &lt;a href=&quot;http://manateegames.co.uk/&quot;&gt;Luke Davies&lt;/a&gt; and &lt;a href=&quot;http://georgiejabbers.com/&quot;&gt;Georgie Aggett&lt;/a&gt; and provided valuable feedback.&lt;/li&gt;
  &lt;li&gt;Those on &lt;a href=&quot;http://forrst.com/posts/Little_project_Ive_been_working_on-sYT&quot;&gt;Forrst&lt;/a&gt; who provided me some confidence in the site design.&lt;/li&gt;
  &lt;li&gt;and many others who I’ve spent hours and hours talking to about it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks guys, you’re awesome.&lt;/p&gt;

&lt;p&gt;You can you &lt;a href=&quot;http://wheresnextapp.com/&quot;&gt;read more at the App’s website here&lt;/a&gt;. Or &lt;a href=&quot;http://itunes.apple.com/gb/app/wheres-next/id454450198?mt=8&quot;&gt;go directly to iTunes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you like it, please mention it to others, it’d be greatly appreciated!&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Starting at Rokk Media</title>
        <link href="https://nickcharlton.net/posts/starting-at-rokk-media.html" />
        <id>https://nickcharlton.net/posts/starting-at-rokk-media.html</id>
        <published>Tue, 26 Jul 2011 00:00:00 +0000</published>
        <updated>Tue, 26 Jul 2011 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Today marks the start of the second week of my placement (actually, the second day of the second week); doing iOS Development at Rokk Media on the outskirts of Exeter.&lt;/p&gt;

&lt;p&gt;My first week has been rather good. The focus so far has been figuring out a few things in iOS on some projects which I hadn’t yet come across (including some sensor stuff and 2D game programming, which is rather interesting). I’ve also pitched in with a couple of web projects.&lt;/p&gt;

&lt;p&gt;I’m currently commuting from Plymouth (via Newton Abbot and then onto Exeter St. Thomas) to the office each day. Whilst it does work, it’s unfortunately quite exhausting, so the intention is to move to Exeter in the next few weeks (any tips would be awesome!).&lt;/p&gt;

&lt;p&gt;So far, all is going well. They’re a great bunch of people to work for, so it looks like I’m about to have a pretty good placement year.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Thoughts on the UoP Intellectual Property Agreement</title>
        <link href="https://nickcharlton.net/posts/thoughts-on-the-uop-intellectual-property-agreement.html" />
        <id>https://nickcharlton.net/posts/thoughts-on-the-uop-intellectual-property-agreement.html</id>
        <published>Mon, 13 Jun 2011 00:00:00 +0000</published>
        <updated>Mon, 13 Jun 2011 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Today, the &lt;a href=&quot;http://www.plymouth.ac.uk/pages/view.asp?page=36193&quot;&gt;University announced that they had entered into an Intellectual Property agreement&lt;/a&gt;. For 10 years. I think this is a terrible idea.&lt;/p&gt;

&lt;h2 id=&quot;good&quot;&gt;Good&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;The University could become a leader in “&lt;a href=&quot;https://twitter.com/danbjorn/status/80291278654423041&quot;&gt;actual, real, commercial products&lt;/a&gt;”.&lt;/li&gt;
  &lt;li&gt;In turn, the University could have more income streams, which could put fees down, or improve resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;bad&quot;&gt;Bad&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Unprofitable but valuable research could be scrapped.&lt;/li&gt;
  &lt;li&gt;The focus for new research projects could be towards quick profits and not long term innovation. So, frivolous, but profitable research could become the mainstay of research groups.&lt;/li&gt;
  &lt;li&gt;Potential to take the very best of the Universities’ lecturers away from teaching and out into external ventures.&lt;/li&gt;
  &lt;li&gt;Potential to remove the option for students in accessing the research groups for final year, or Masters projects.&lt;/li&gt;
  &lt;li&gt;By pushing the IP out into other ventures, the University will no longer be able to take direct credit for the achievements of various projects.&lt;/li&gt;
  &lt;li&gt;The research groups of the University can be a pulling force for new students, by pushing out the interests might stop that happening.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It looks like we’re stuck with it. But let’s hope it’s not contagious. The sight of a country of profit driven research feels pretty grim, to me. On top of that, let’s hope the good points come to be true, rather than the possible downsides. That will come in the future.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Digital Peninsula Unconference III, Falmouth</title>
        <link href="https://nickcharlton.net/posts/digital-peninsula-unconference-iii-falmouth.html" />
        <id>https://nickcharlton.net/posts/digital-peninsula-unconference-iii-falmouth.html</id>
        <published>Sun, 12 Jun 2011 00:00:00 +0000</published>
        <updated>Sun, 12 Jun 2011 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Yesterday was the third iteration of the Digital Peninsula Unconference, at &lt;a href=&quot;http://www.falmouth.ac.uk/&quot;&gt;University College Falmouth, Tremough&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once again, it was a great event, with a panel/session on Agile methodologies, a focus on two possible projects (website and store), and once again a set of lightening talks by various people. Then there was a BBQ down on the beach (though, I didn’t go to that bit.)&lt;/p&gt;

&lt;p&gt;I also took photos this time. The lighting was much better at this venue than in &lt;a href=&quot;http://nickcharlton.net/post/digital-peninsula-unconference-ii-exeter&quot;&gt;Exeter&lt;/a&gt;, but there still was a lot of blurry shots. They certainly could have been better (shooting these things with a 50mm is quite an impractical pain…), but I’m not too displeased with them.&lt;/p&gt;

&lt;p&gt;Anyway; &lt;a href=&quot;http://www.flickr.com/photos/nickcharlton/sets/72157626820736307/&quot;&gt;you can find them here on Flickr&lt;/a&gt;, as always.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Digital Peninsula Unconference II, Exeter</title>
        <link href="https://nickcharlton.net/posts/digital-peninsula-unconference-ii-exeter.html" />
        <id>https://nickcharlton.net/posts/digital-peninsula-unconference-ii-exeter.html</id>
        <published>Sun, 15 May 2011 00:00:00 +0000</published>
        <updated>Sun, 15 May 2011 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;On Saturday, the second version of the &lt;a href=&quot;http://lanyrd.com/2011/digpenII/&quot;&gt;Digital Peninsula Unconference&lt;/a&gt; was hosted in Exeter, at the Phoenix. After being &lt;a href=&quot;http://twitter.com/#!/teddilybear/status/68722214702301185&quot;&gt;volunteered to take some photographs&lt;/a&gt;, I spent much of the event running about taking them (that’s the flaw in having only a 50mm lens available to you). I’ve posted them on Flickr, but below are a few of the ones I liked best.&lt;/p&gt;

&lt;p&gt;The conference overall was pretty good, although it did feel like some of the people were pitching a little bit too much. Given the mostly technical community, it would be nice to hear more about the decisions made behind projects - stuff that’s useful to all.&lt;/p&gt;

&lt;figure&gt;
&lt;a href=&quot;https://www.flickr.com/photos/nickcharlton/5722585712/&quot; title=&quot;On Stage Tech Support by nickcharlton, on Flickr&quot;&gt;&lt;img src=&quot;https://farm3.static.flickr.com/2091/5722585712_953fd4b22c.jpg&quot; width=&quot;500&quot; height=&quot;334&quot; alt=&quot;On Stage Tech Support&quot; /&gt;&lt;/a&gt;
&lt;figcaption&gt;A little bit of on stage tech support.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
&lt;a href=&quot;https://www.flickr.com/photos/nickcharlton/5722002007/&quot; title=&quot;The Waving Crowd by nickcharlton, on Flickr&quot;&gt;&lt;img src=&quot;https://farm3.static.flickr.com/2412/5722002007_1054243cfc.jpg&quot; width=&quot;334&quot; height=&quot;500&quot; alt=&quot;The Waving Crowd&quot; /&gt;&lt;/a&gt;
&lt;figcaption&gt;The Waving Crowd&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;a href=&quot;http://www.flickr.com/photos/nickcharlton/sets/72157626728344858/&quot;&gt;You can see the rest of the images here on Flickr.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All of these were shot on a Canon 400D, with a 50mm lens. They were shot at ISO1600. It was quite a dark room, so many of the shots taken didn’t work out too well. In other news, if &lt;a href=&quot;http://nickcharlton.net/contact&quot;&gt;someone wants me to build something for them&lt;/a&gt;, I could probably get a better lens.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Building Custom Android ListViews</title>
        <link href="https://nickcharlton.net/posts/building-custom-android-listviews.html" />
        <id>https://nickcharlton.net/posts/building-custom-android-listviews.html</id>
        <published>Wed, 11 May 2011 00:00:00 +0000</published>
        <updated>Wed, 11 May 2011 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;The documentation for Android’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListView&lt;/code&gt;’s is a little sparse. The examples around on the web are also not too great. This article intends to be the one I was searching for in trying to understand how to display some more advanced data, and deal with events.&lt;/p&gt;

&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListView&lt;/code&gt;’s are the solution to most data problems on Android (much like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UITableView&lt;/code&gt; is used extensively in iOS). However, they are a little undocumented. The rest of this article should allow you to go from having a basic &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListView&lt;/code&gt;, to one much more complex and useful.&lt;/p&gt;

&lt;h3 id=&quot;the-project&quot;&gt;The Project&lt;/h3&gt;

&lt;p&gt;This is implemented using Android 2.2, but it will work with more recent versions. These are the project settings used:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Project name: HelloListView
Build target: Android 2.2
Application name: CustomListViews
Package name: org.example.hellolistview
Create Activity: HelloListView
Min SDK Version: 8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h3&gt;

&lt;p&gt;Firstly, I assume you have tried building a basic &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListView&lt;/code&gt; (&lt;a href=&quot;http://developer.android.com/resources/tutorials/views/hello-listview.html&quot;&gt;This tutorial is good for that&lt;/a&gt;). Secondly, I assume your data source is an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ArrayList&lt;/code&gt;, containing objects for each element of data.&lt;/p&gt;

&lt;p&gt;Once you have followed the basics of this guide, you will find that you can use the latter sections as you wish.&lt;/p&gt;

&lt;p&gt;At 2010’s Google IO, there was an hour long session which talked about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListView&lt;/code&gt;’s extensively, you may wish to watch that first. &lt;a href=&quot;http://www.google.com/events/io/2010/sessions/world-of-listview-android.html&quot;&gt;You’ll find it here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;building-the-foundations&quot;&gt;Building the Foundations&lt;/h2&gt;

&lt;p&gt;This starts with building a basic view which is backed onto an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ArrayList&lt;/code&gt;. You can expand on the complexity from here.&lt;/p&gt;

&lt;h3 id=&quot;extending-listactivity&quot;&gt;Extending ListActivity&lt;/h3&gt;

&lt;p&gt;The first step is to subclass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListActivity&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Activity&lt;/code&gt;. This provides us with some functionality specific to lists. Of note, if we have no data we can easily provide an alternative.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public class HelloListView extends ListActivity {
    // ....
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;the-view-xml-mainxml-list_itemxml&quot;&gt;The View XML (main.xml, list_item.xml)&lt;/h3&gt;

&lt;h4 id=&quot;mainxml&quot;&gt;main.xml&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;

&amp;lt;LinearLayout 
	xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    android:layout_width=&quot;fill_parent&quot;
    android:layout_height=&quot;fill_parent&quot;&amp;gt;
    
    &amp;lt;ListView 
    	android:id=&quot;@android:id/list&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot; /&amp;gt;
    
    &amp;lt;TextView
    	android:id=&quot;@android:id/empty&quot;
    	android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;@string/empty&quot; /&amp;gt;
&amp;lt;/LinearLayout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This defines the main view. It contains a layout which fills the screen, which in turn contains two subviews. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListView&lt;/code&gt; defines the view, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TextView&lt;/code&gt; is the backup which is called by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListActivity&lt;/code&gt; when there is no data to display.&lt;/p&gt;

&lt;h4 id=&quot;list_itemxml&quot;&gt;list_item.xml&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;

&amp;lt;LinearLayout 
	xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
	android:layout_width=&quot;wrap_content&quot; 
	android:layout_height=&quot;wrap_content&quot; 
	android:background=&quot;#000000&quot;&amp;gt;
	&amp;lt;TextView 
		android:layout_width=&quot;wrap_content&quot;
		android:layout_height=&quot;wrap_content&quot; 
		android:id=&quot;@+id/text&quot;&amp;gt;
	&amp;lt;/TextView&amp;gt;
&amp;lt;/LinearLayout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This defines a very basic view. A screenshot is shown below. You do need to provide &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;android:layout_width&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;android:layout_height&lt;/code&gt; declarations for each, otherwise it will not render.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;/resources/android-list-views/listview_foundations.png&quot; alt=&quot;ListView Basics&quot; /&gt;
&lt;figcaption&gt;ListView Basics&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;data-source-and-adapter&quot;&gt;Data Source and Adapter&lt;/h3&gt;

&lt;p&gt;To display what is shown in the screenshot above, the following code is used. It’s not necessarily the most concise, but you should find it simple to follow.&lt;/p&gt;

&lt;h4 id=&quot;hellolistviewjava&quot;&gt;HelloListView.java&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public class HelloListView extends ListActivity {
	// define the data source
	private ArrayList&amp;lt;String&amp;gt; data;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // setup the data source
        this.data = new ArrayList&amp;lt;String&amp;gt;();
        
        // add some objects into the array list
        this.data.add(&quot;List Item 1&quot;);
        this.data.add(&quot;List Item 2&quot;);
        this.data.add(&quot;List Item 3&quot;);
        
        // use main.xml for the layout
        setContentView(R.layout.main);
        
        // setup the data adaptor
        ArrayAdapter&amp;lt;String&amp;gt; adapter = new ArrayAdapter&amp;lt;String&amp;gt;(this, R.layout.list_item, R.id.text, this.data);
        
        // specify the list adaptor
        setListAdapter(adapter);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, we are creating the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ArrayList&lt;/code&gt; holding our data set. Then we are adding the three elements we wish to display.&lt;/p&gt;

&lt;p&gt;After this, we are setting up the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ArrayAdapter&lt;/code&gt; to bridge the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListView&lt;/code&gt; to the dataset, and the specific item in the list.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ArrayAdaptor&lt;/code&gt; translates the objects given to it (the last parameter) using a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.toString()&lt;/code&gt;. This places the value inside the element with id &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@+id/text&lt;/code&gt; inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list_item.xml&lt;/code&gt; file.&lt;/p&gt;

&lt;h2 id=&quot;displaying-custom-objects&quot;&gt;Displaying Custom Objects&lt;/h2&gt;

&lt;p&gt;For the rest of this article, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListItem&lt;/code&gt; class is going to be used to display inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListView&lt;/code&gt;. This contains two members, title and subtitle. This is enough to show off using a custom adaptor.&lt;/p&gt;

&lt;h3 id=&quot;listitemjava&quot;&gt;ListItem.java&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/*
* Defines a simple object to be displayed in a list view.
*/
package org.example.HelloListView;

public class ListItem {
	public String title;
	public String subTitle;
	
	// default constructor
	public ListItem() {
		this(&quot;Title&quot;, &quot;Subtitle&quot;);
	}
	
	// main constructor
	public ListItem(String title, String subTitle) {
		super();
		this.title = title;
		this.subTitle = subTitle;
	}
	
	// String representation
	public String toString() {
		return this.title + &quot; : &quot; + this.subTitle;
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are also a few changes to be made to the rest of the project to get this to work.&lt;/p&gt;

&lt;h3 id=&quot;hellolistviewjava-1&quot;&gt;HelloListView.java&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// setup the data source
this.data = new ArrayList&amp;lt;ListItem&amp;gt;();

// add some objects into the array list
ListItem item = new ListItem(&quot;Hello&quot;, &quot;Nick&quot;);
this.data.add(item);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You will also need to change any references to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ArrayList&amp;lt;String&amp;gt;&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ArrayList&amp;lt;ListItem&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By default, this will still display in the current incarnation. This is because we’re simply outputting a string representation of the object. To display more complex information, the &lt;a href=&quot;http://developer.android.com/reference/android/widget/SimpleAdapter.html&quot;&gt;SimpleAdaptor class&lt;/a&gt; can render checkable objects (like a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CheckBox&lt;/code&gt;), Strings and Images. Anything more complicated would require building a custom data adaptor.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;/resources/android-list-views/listview_custom_object.png&quot; alt=&quot;ListView Custom Object&quot; /&gt;
&lt;figcaption&gt;ListView Custom Object&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;complex-listviews-with-simpleadapter&quot;&gt;Complex ListViews with SimpleAdapter&lt;/h2&gt;

&lt;p&gt;SimpleAdapter can be used for building more complex &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListViews&lt;/code&gt;. Compared to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ArrayAdapter&lt;/code&gt; class, SimpleAdaptor takes a few more arguments to map more data to more views.&lt;/p&gt;

&lt;p&gt;Unfortunately, SimpleAdapter requires a collection of Maps to define the data. There are two ways to put together the objects wanted, the first is to create all new objects, and the second is to build a simple wrapper around Map on our original &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListItem&lt;/code&gt; class. The former is shown below:&lt;/p&gt;

&lt;h3 id=&quot;creating-new-objects&quot;&gt;Creating new Objects&lt;/h3&gt;

&lt;h4 id=&quot;hellolistviewjava-2&quot;&gt;HelloListView.java&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public class HelloListView extends ListActivity {
	// define the data source
	private ArrayList&amp;lt;Map&amp;gt; data;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // setup the data source
        this.data = new ArrayList&amp;lt;Map&amp;gt;();
        
        // add some objects into the array list
        Map m = new HashMap();
        m.put(&quot;title&quot;, &quot;Hello&quot;);
        m.put(&quot;subtitle&quot;, &quot;Nick&quot;);
        
        this.data.add(m);
        
        // use main.xml for the layout
        setContentView(R.layout.main);
        
        // setup the data adaptor
        String[] from = {&quot;title&quot;, &quot;subtitle&quot;};
        int[] to = {R.id.title, R.id.subtitle};
        SimpleAdapter adapter = new SimpleAdapter(this, (List&amp;lt;? extends Map&amp;lt;String, ?&amp;gt;&amp;gt;) this.data, R.layout.list_item, from, to);
        
        // specify the list adaptor
        setListAdapter(adapter);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;list_itemxml-1&quot;&gt;list_item.xml&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;

&amp;lt;LinearLayout 
	xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
	android:layout_width=&quot;wrap_content&quot; 
	android:layout_height=&quot;wrap_content&quot; 
	android:background=&quot;#000000&quot;&amp;gt;
	
	&amp;lt;TextView 
		android:layout_width=&quot;wrap_content&quot;
		android:layout_height=&quot;wrap_content&quot; 
		android:id=&quot;@+id/title&quot;&amp;gt;
	&amp;lt;/TextView&amp;gt;
	
	&amp;lt;TextView 
		android:layout_width=&quot;wrap_content&quot;
		android:layout_height=&quot;wrap_content&quot; 
		android:id=&quot;@+id/subtitle&quot;&amp;gt;
	&amp;lt;/TextView&amp;gt;
		
&amp;lt;/LinearLayout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, we are creating a new object (which is a Map), and placing this into our collection. This is then passed into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SimpleAdapter&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;extending-map&quot;&gt;Extending Map&lt;/h3&gt;

&lt;p&gt;By extending Map, we can adjust our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListItem&lt;/code&gt; class to appear to be a Map. Here, we will use the members of the class as the key, and their values, as the values.&lt;/p&gt;

&lt;h4 id=&quot;listitemjava-1&quot;&gt;ListItem.java&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public class ListItem implements Map&amp;lt;String, String&amp;gt; {
	public String title;
	public String subTitle;
	
	// default constructor
	public ListItem() {
		this(&quot;Title&quot;, &quot;Subtitle&quot;);
	}
	
	// main constructor
	public ListItem(String title, String subTitle) {
		super();
		this.title = title;
		this.subTitle = subTitle;
	}
	
	// String representation
	public String toString() {
		return this.title + &quot; : &quot; + this.subTitle;
	}
	
	// Map interface classes
	
	// return a count of our members
	public int size() {
		return 2;
	}
	
	// set the values of the object to null
	public void clear() {
		this.title = null;
		this.subTitle = null;
	}
	
	// return all of the values as a collection
	public ArrayList&amp;lt;String&amp;gt; values() {
		ArrayList&amp;lt;String&amp;gt; list = new ArrayList&amp;lt;String&amp;gt;();
		
		list.add(title);
		list.add(subTitle);
		
		return list;
	}
	
	// if the values of the members are null, return true
	public boolean isEmpty() {
		if ((this.title == null) &amp;amp;&amp;amp; (this.subTitle == null)) {
			return true;
		} else {
			return false;
		}
	}
	
	// return a set of the members
	public Set&amp;lt;String&amp;gt; keySet() {
		Set&amp;lt;String&amp;gt; s = new HashSet&amp;lt;String&amp;gt;();
		
		s.add(&quot;title&quot;);
		s.add(&quot;subTitle&quot;);
		
		return s;
	}
	
	// return a set of the member values
	public Set entrySet() {
		Set&amp;lt;String&amp;gt; s = new HashSet&amp;lt;String&amp;gt;();
		
		s.add(this.title);
		s.add(this.subTitle);
		
		return s;
	}
	
	// return the value of the given key
	public String get(Object key) {
		if (key.equals(&quot;title&quot;)) {
			return this.title;
		}
		if (key.equals(&quot;subTitle&quot;)) {
			return this.subTitle;
		}
		// if we can&apos;t return a value, throw the exception
		throw new ClassCastException();
	}
	
	// set the value of a given key
	public String put(String key, String value) {
		if (key.equals(&quot;title&quot;)) {
			this.title = value;
		}
		if (key.equals(&quot;subTitle&quot;)) {
			this.subTitle = value;
		}
		return value;
	}
	
	// remove a key (nullify)
	public String remove(Object key) {
		String value = null;
		if (key.equals(&quot;title&quot;)) {
			value = this.title;
			this.title = null;
		}
		if (key.equals(&quot;subTitle&quot;)) {
			value = this.subTitle;
			this.subTitle = null;
		}
		return value;
	}
	
	// return boolean if we have a member
	public boolean containsKey(Object key) {
		if (key.equals(&quot;title&quot;)) {
			return true;
		}
		if (key.equals(&quot;subTitle&quot;)) {
			return true;
		}
		return false;
	}
	
	// return boolean if we have a member&apos;s value
	public boolean containsValue(Object value) {
		if (value.equals(this.title)) {
			return true;
		}
		if (value.equals(this.subTitle)) {
			return true;
		}
		return false;
	}

	// set the values of this map to that of another
	public void putAll(Map&amp;lt;? extends String, ? extends String&amp;gt; arg0) {
		// we only need the stub.
	}
	
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This implements all of the methods required by the &lt;a href=&quot;http://download.oracle.com/javase/6/docs/api/java/util/Map.html&quot;&gt;Map interface&lt;/a&gt;. You may not need all of them to support SimpleAdapter. I’d suggest subclassing the class above, and making this abstract. Then you can implement just what you need.&lt;/p&gt;

&lt;p&gt;In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HelloListView.java&lt;/code&gt;, the original object, then map declarations can then be replaced with:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// add some objects into the array list
ListItem item = new ListItem(&quot;Hello&quot;, &quot;Nick&quot;);
    
this.data.add(item);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The view will then look like the previous screenshot.&lt;/p&gt;

&lt;h2 id=&quot;creating-a-custom-data-adaptor&quot;&gt;Creating a Custom Data Adaptor&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ArrayAdapter&lt;/code&gt; provides a simple way to add an array of strings to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListView&lt;/code&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SimpleAdapter&lt;/code&gt; provides a way to specify a more complex object (mostly containing strings) and place those into a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListView&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, if you want to do anything more complicated you need to roll your own Adaptor. The Data Adaptor provides the link between the data and the View. It implements the methods of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BaseAdapter&lt;/code&gt; to provide what is needed by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListView&lt;/code&gt;. Here, we are assuming that you wish to stick with XML for layout (it’s the suggested way). If you wish to do it just in code, &lt;a href=&quot;http://www.anddev.org/novice-tutorials-f8/checkbox-text-list-extension-of-iconified-text-tutorial-t771.html&quot;&gt;here’s an example&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;For this section, we are starting again with the basic list view implemented earlier.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;hellolistviewjava-3&quot;&gt;HelloListView.java&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public class HelloListView extends ListActivity {
	private ArrayList&amp;lt;ListItem&amp;gt; data;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        // setup the data source
        this.data = new ArrayList&amp;lt;ListItem&amp;gt;();
        
        // create some objects
        ListItem item1 = new ListItem(&quot;Title&quot;, &quot;Subtitle&quot;);
        
        // add them into the array list
        this.data.add(item1);
        
        // use main.xml for the layout
        setContentView(R.layout.main);
        
        // setup the data adaptor
        CustomAdapter adapter = new CustomAdapter(this, R.layout.list_item, this.data);
        
        // specify the list adaptor
        setListAdapter(adapter);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;customadapterjava&quot;&gt;CustomAdapter.java&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public class CustomAdapter extends BaseAdapter {
	// store the context (as an inflated layout)
	private LayoutInflater inflater;
	// store the resource (typically list_item.xml)
	private int resource;
	// store (a reference to) the data
	private ArrayList&amp;lt;ListItem&amp;gt; data;
	
	/**
	 * Default constructor. Creates the new Adaptor object to
	 * provide a ListView with data.
	 * @param context
	 * @param resource
	 * @param data
	 */
	public CustomAdapter(Context context, int resource, ArrayList&amp;lt;ListItem&amp;gt; data) {
		this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		this.resource = resource;
		this.data = data;
	}
	
	/**
	 * Return the size of the data set.
	 */
	public int getCount() {
		return this.data.size();
	}
	
	/**
	 * Return an object in the data set.
	 */
	public Object getItem(int position) {
		return this.data.get(position);
	}
	
	/**
	 * Return the position provided.
	 */
	public long getItemId(int position) {
		return position;
	}

	/**
	 * Return a generated view for a position.
	 */
	public View getView(int position, View convertView, ViewGroup parent) {
		// reuse a given view, or inflate a new one from the xml
		View view;
		 
		if (convertView == null) {
			view = this.inflater.inflate(resource, parent, false);
		} else {
			view = convertView;
		}
		
		// bind the data to the view object
		return this.bindData(view, position);
	}
	
	/**
	 * Bind the provided data to the view.
	 * This is the only method not required by base adapter.
	 */
	public View bindData(View view, int position) {
		// make sure it&apos;s worth drawing the view
		if (this.data.get(position) == null) {
			return view;
		}
		
		// pull out the object
		ListItem item = this.data.get(position);
		
		// extract the view object
		View viewElement = view.findViewById(R.id.title);
		// cast to the correct type
		TextView tv = (TextView)viewElement;
		// set the value
		tv.setText(item.title);
		
		viewElement = view.findViewById(R.id.subTitle);
		tv = (TextView)viewElement;
		tv.setText(item.subTitle);
		
		// return the final view object
		return view;
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;list_itemxml-2&quot;&gt;list_item.xml&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;

&amp;lt;LinearLayout 
   	xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
   	android:layout_width=&quot;wrap_content&quot; 
   	android:layout_height=&quot;wrap_content&quot; 
   	android:background=&quot;#000000&quot;&amp;gt;
   	&amp;lt;TextView 
   		android:layout_width=&quot;wrap_content&quot;
   		android:layout_height=&quot;wrap_content&quot; 
   		android:id=&quot;@+id/title&quot;&amp;gt;
   	&amp;lt;/TextView&amp;gt;
   	&amp;lt;TextView 
   		android:layout_width=&quot;wrap_content&quot;
   		android:layout_height=&quot;wrap_content&quot; 
   		android:id=&quot;@+id/subTitle&quot;&amp;gt;
   	&amp;lt;/TextView&amp;gt;
&amp;lt;/LinearLayout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The same source as above was used for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListItem&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bindData&lt;/code&gt; method is where the most customisation will be required. This extracts the given view objects (the XML TextView, in this case), and binds the value of the members to it. Here, we have just used TextViews, but something similar would be used for other parts of the view.&lt;/p&gt;

&lt;h2 id=&quot;handling-events&quot;&gt;Handling Events&lt;/h2&gt;

&lt;h3 id=&quot;single-taps&quot;&gt;Single Taps&lt;/h3&gt;

&lt;p&gt;The most obvious case for handling events is handling when a user taps (or clicks) a row. To do this, you define the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;onItemClick()&lt;/code&gt; method, and then inside this you can extract the original object back out to do stuff with it.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    ListView lv = getListView();
    lv.setTextFilterEnabled(true);

    lv.setOnItemClickListener(new OnItemClickListener() {
      public void onItemClick(AdapterView&amp;lt;?&amp;gt; parent, View view, int position, long id) {
    	  // When clicked, show a toast with the TextView text
    	  Toast.makeText(getApplicationContext(), parent.getItemAtPosition(position).toString(),
            Toast.LENGTH_SHORT).show();
      }
    });
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This defines the on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;onItemClick()&lt;/code&gt; method for handling the event. When a user taps on the item, it prints out the string representation of the object. It uses &lt;a href=&quot;http://developer.android.com/reference/android/widget/Toast.html&quot;&gt;“Toast”, Android’s discrete notifications class&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The important call is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parent.getItemAtPosition(position)&lt;/code&gt;. This extracts the selected object from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListAdapter&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;long-taps&quot;&gt;Long Taps&lt;/h3&gt;

&lt;p&gt;“long taps” are perceived to be the equivalent to right-clicks on the desktop. On a list item, you would take this to mean a desire for more information about an item.&lt;/p&gt;

&lt;p&gt;Implementing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LongClick&lt;/code&gt; is much the same as normal clicks (taps). The difference is merely in the method calls which are defined:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;lv.setOnItemLongClickListener(new OnItemLongClickListener() {
    public boolean onItemLongClick(AdapterView&amp;lt;?&amp;gt; parent, View view, int position, long id) {
      	  // When clicked, show a toast with the TextView text
      	  Toast.makeText(getApplicationContext(), &quot;You long clicked on: &quot; + parent.getItemAtPosition(position).toString(),
              Toast.LENGTH_SHORT).show();
      	  
      	  return true;
    }
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;adjusting-data-in-the-view&quot;&gt;Adjusting Data in the View&lt;/h2&gt;

&lt;p&gt;This is a bit of a hack. The intention here is to show how to add and notify the adaptor of changes, rather than suggest a good way to go about doing it.&lt;/p&gt;

&lt;h3 id=&quot;adding-new-data&quot;&gt;Adding New Data&lt;/h3&gt;

&lt;p&gt;The simplest way to demonstrate this is to make the list item duplicate itself on tap (or click). Add the following before the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Toast&lt;/code&gt; declarations, and the data model will be updated:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// on press, duplicate the object
ListItem item = (ListItem)parent.getItemAtPosition(position);
ListItem newItem = new ListItem(item.title, item.subTitle);
data.add(newItem);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The next step is to notify the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListView&lt;/code&gt; that the data is invalid. This will reload the data from the data model, and the new object will appear.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// get the adaptor
SimpleAdapter adapter = (SimpleAdapter)parent.getAdapter();
adapter.notifyDataSetChanged();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The same goes for handling editing and deleting functions on the underlying data. You just need to make sure you keep adjust the data set, and keep the adapter informed - then the most recent data is both saved and displayed.&lt;/p&gt;

&lt;h2 id=&quot;code&quot;&gt;Code&lt;/h2&gt;

&lt;p&gt;For each of the sections in this article, I have put together a set of examples. They are Zip archives of the Eclipse projects which were created whilst I was writing this.&lt;/p&gt;

&lt;p&gt;The projects are targeted at Android 2.2, and were used with Eclipse 3.5.2 (Helios). The code is licensed under the MIT license.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://nickcharlton.net/resources/android-list-views/code.zip&quot;&gt;You can find the code here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading&lt;/h2&gt;

&lt;p&gt;To move along from here, I would suggest reading Chapter 9 (Putting SQL to Work) of &lt;a href=&quot;http://www.amazon.co.uk/gp/product/1934356565/ref=as_li_ss_tl?ie=UTF8&amp;amp;tag=nisbl-21&amp;amp;linkCode=as2&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=1934356565&quot;&gt;Hello, Android (3rd Edition) by Ed Burnette&lt;/a&gt;. This provides a basic introduction to ListViews, but more importantly talks about hooking up a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListView&lt;/code&gt; to &lt;a href=&quot;http://www.sqlite.org/&quot;&gt;SQLite&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As mentioned earlier, the &lt;a href=&quot;http://developer.android.com/resources/tutorials/views/hello-listview.html&quot;&gt;basic ListView tutorial&lt;/a&gt; and the &lt;a href=&quot;http://developer.android.com/reference/android/app/ListActivity.html&quot;&gt;ListActivity Class Reference&lt;/a&gt; should also be of use to you.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Android’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListView&lt;/code&gt; is pretty powerful, unfortunately, the documentation isn’t go great. Hopefully this will give people new to Android a kick-start in using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListView&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href=&quot;http://phalt.co.uk/&quot;&gt;Paul Hallet&lt;/a&gt; for reviewing this before posting.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Review: Arduino Cookbook by Michael Margolis</title>
        <link href="https://nickcharlton.net/posts/review-arduino-cookbook-by-michael-margolis.html" />
        <id>https://nickcharlton.net/posts/review-arduino-cookbook-by-michael-margolis.html</id>
        <published>Tue, 22 Mar 2011 00:00:00 +0000</published>
        <updated>Tue, 22 Mar 2011 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;For a while, I’ve wanted to play around with the &lt;a href=&quot;http://arduino.cc/&quot;&gt;Arduino&lt;/a&gt;; the open source embedded hardware that’s stuck it’s claws into everything from the arts, to personal projects and much more. Then, &lt;a href=&quot;http://oreilly.com/catalog/9780596802486/&quot;&gt;Arduino Cookbook&lt;/a&gt; came up on the O’Reilly Blogger Review Program, and I couldn’t say no to having a pushed look into it.&lt;/p&gt;

&lt;p&gt;I think the Arduino Cookbook will fill the void between books for which their intention is to cover lots of programming fundamentals, and books which only cover the Arduino boards with a cursory mention.&lt;/p&gt;

&lt;p&gt;Like most of O’Reilly’s cookbooks, they intend to provide a group of solutions to common problems. Sometimes they feel a little filled out; the thought that this recipe and solution are only in there to fill a gap often comes to mind. With my limited Arduino experience, I didn’t see this as much as with others, as a somewhat experienced programmer, the programming Chapters did feel a little bit long, but what is there does cover issues where you’d rather have an instant answer than not understand why something is the case.&lt;/p&gt;

&lt;p&gt;Overall, I was impressed by what it covered. Notably, serial communication, sensors, driving motors, I2C communication and networking stand out. These are all required for the few projects I have in mind.&lt;/p&gt;

&lt;p&gt;If you have any experience with lower-level microcontrollers (I have a little bit of experience with C on Microchips’ PIC24 chip), you’ll find some more advanced stuff like interrupts and timers towards the end. This is a welcome addition.&lt;/p&gt;

&lt;p&gt;The appendixes should be rather useful for most; they cover a basic introduction to electronics, but, as the author says himself, it’s not a replacement for a proper book on the subject.&lt;/p&gt;

&lt;p&gt;The author suggests that it would be good for a variety of readers, personally, I think it will slot rather nicely into the likes of most programmers - especially those who wish to add something physical to their projects.&lt;/p&gt;

&lt;p&gt;If you are looking for something to help complete a project, or just get started with the Arduino quickly this is probably the best bet. You’ll find plenty in here to get you going.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;In the spirit of transparency, this review was written for the O’Reilly Blogger Review Program, because of that, they gave me a copy of the ebook for free.&lt;/em&gt;&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>The Digital Peninsula&apos;s First Web Unconference</title>
        <link href="https://nickcharlton.net/posts/the-digital-peninsulas-first-web-unconference.html" />
        <id>https://nickcharlton.net/posts/the-digital-peninsulas-first-web-unconference.html</id>
        <published>Mon, 07 Mar 2011 00:00:00 +0000</published>
        <updated>Mon, 07 Mar 2011 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;On Saturday I gave a quick talk about &lt;a href=&quot;http://termisoc.org/&quot;&gt;TermiSoc&lt;/a&gt; at the &lt;a href=&quot;http://medworm.eventbrite.com/&quot;&gt;Unconference&lt;/a&gt; in the University. &lt;a href=&quot;https://twitter.com/#!/frankiedolan&quot;&gt;Frankie&lt;/a&gt; put together a great event, with a really good turn out, with more than 80 people showing up, from down in Cornwall, all the way up to Bristol.&lt;/p&gt;

&lt;p&gt;It was my first time speaking in front of more than about ten people. And as I quipped on Twitter just after, “&lt;a href=&quot;http://twitter.com/#!/nickcharlton/status/44047505373151232&quot;&gt;OH GOD, THATS A LOT OF PEOPLE&lt;/a&gt;”. I did however enjoy it enough that I would likely agree to do it again.&lt;/p&gt;

&lt;p&gt;Afterwards a couple of people came up to ask me about the kinds of people in TermiSoc, and many more mentioned that they’d wanted to come to one of our Monday Workspace evenings.&lt;/p&gt;

&lt;p&gt;As fluffy as it sounds, I hope that we can stay connected with the rest of the South West Web community, they’re all a great bunch.&lt;/p&gt;

&lt;p&gt;The general complaint was that it just wasn’t long enough, which I totally understand, I would like to have talked to many more people.&lt;/p&gt;

&lt;p&gt;The next event is apparently going to take place in Exeter, so see you all there!&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Using ruby-oci8 on Ubuntu/Debian</title>
        <link href="https://nickcharlton.net/posts/using-rubyoci8-on-ubuntudebian.html" />
        <id>https://nickcharlton.net/posts/using-rubyoci8-on-ubuntudebian.html</id>
        <published>Wed, 02 Mar 2011 00:00:00 +0000</published>
        <updated>Wed, 02 Mar 2011 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;With this year’s integrating project, we were required to write a web service to integrate Android with the University’s Oracle server. After asking to use Ruby (and succeeding), I was then left with the obstacle of hooking up to the Oracle database. These a few notes on getting this working.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: You’ll need to follow these steps if you are installing via the &lt;a href=&quot;http://rubygems.org/gems/ruby-oci8&quot;&gt;gem&lt;/a&gt; or &lt;a href=&quot;http://ruby-oci8.rubyforge.org/en/&quot;&gt;doing it manually&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Firstly; you’ll need to install the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libaio-dev&lt;/code&gt; package, as instant client relies upon it.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo apt-get install libaio-dev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;oracle-instant-client&quot;&gt;Oracle Instant Client&lt;/h2&gt;

&lt;p&gt;After this, you’ll need to &lt;a href=&quot;http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html&quot;&gt;pick up the Instant Client Basic and Instant Client SDK packages from Oracle’s site&lt;/a&gt;. &lt;em&gt;Note: You’ll need to register to access these.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Jump into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/opt/oracle&lt;/code&gt; and extract them.&lt;/p&gt;

&lt;p&gt;You will then end up with a folder such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;instant-client_11_2&lt;/code&gt; (the SDK will end up in the same folder).&lt;/p&gt;

&lt;p&gt;Inside this folder, you will want to create a symlink to the current version of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libclntsh.so.*&lt;/code&gt; library:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo ln -s libclntsh.so.11.1 libclntsh.so
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;add-to-your-path&quot;&gt;Add to your PATH&lt;/h2&gt;

&lt;p&gt;To tell Ruby where to find the instant client libraries, you need to add the newly setup folder into your PATH. You can do this by doing something similar to the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;export LD_LIBRARY_PATH=/opt/oracle/instantclient_11_2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you don’t add the libraries to your PATH, Ruby will not be able to access them. If you don’t add them to something like your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.bashrc&lt;/code&gt;, they will be forgotten on reboot and when using the OCI8 gem, Ruby will complain at you.&lt;/p&gt;

&lt;h2 id=&quot;install-the-gem&quot;&gt;Install the gem&lt;/h2&gt;

&lt;p&gt;Next you need to install the Ruby library itself. You can find out about &lt;a href=&quot;http://ruby-oci8.rubyforge.org/en/InstallForInstantClient.html&quot;&gt;compiling it yourself here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To install you will need superuser access, however &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo&lt;/code&gt; will not pass in the library location, to get around this we can deliberately pass in the library path to the gem installer.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo env LD_LIBRARY_PATH=/opt/oracle/instantclient_11_2 gem install ruby-oci8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;At this point, everything should be working. Shared Library errors are generally caused by an issue with Oracle’s instant client, especially when the PATH has been reset. This article was based on a few others, you can &lt;a href=&quot;http://ruby-oci8.rubyforge.org/en/InstallForInstantClient.html&quot;&gt;read&lt;/a&gt; &lt;a href=&quot;http://www.it-wikipedia.com/web/how-to-install-ruby-oci8-on-ubuntu-server.html&quot;&gt;those&lt;/a&gt; &lt;a href=&quot;http://2muchtea.wordpress.com/2007/12/23/installing-ruby-oci8-on-ubuntu/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>TermiSoc Hack Weekend 2011</title>
        <link href="https://nickcharlton.net/posts/termisoc-hack-weekend-2011.html" />
        <id>https://nickcharlton.net/posts/termisoc-hack-weekend-2011.html</id>
        <published>Tue, 08 Feb 2011 00:00:00 +0000</published>
        <updated>Tue, 08 Feb 2011 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;em&gt;I intend for this post to hold all of the presentations and sessions that we’ve hosted for the TermiSoc Hack Weekend 2011. This post will expand in the future as more content is covered.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;Previously, this was an embedded iWork document, but it’s been lost to 
history. But it’s very out of date now.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This first session was intended to explain the overall plan, how I intended to 
approach the prior sessions, but to overall drum up some enthusiasm about what 
we should build.&lt;/p&gt;

&lt;p&gt;Overall, we ended up with a set of ideas ranging from games, to Arduino projects, to more complicated stuff using open data sets, algorithms and natural language processing.&lt;/p&gt;

&lt;p&gt;The plan so far is to target mobile devices, add in location services and then add some element of gaming to it. This means that I will likely run some sessions covering things from HTML5/CSS &amp;amp; JavaScript to Python for handling the backend. If I get time, I’ll likely cover some Google App Engine stuff, too.&lt;/p&gt;

&lt;p&gt;It really is however based upon the idea itself. Something that will be talked about on the &lt;a href=&quot;http://lists.termisoc.org/listinfo/termilist&quot;&gt;mailing list&lt;/a&gt;, over the next week or so.&lt;/p&gt;

&lt;p&gt;If you’re reading this and not part of &lt;a href=&quot;http://termisoc.org/&quot;&gt;TermiSoc&lt;/a&gt;, feel free to give me a shout. I don’t wish to run a closed event, but I also hope that people will show up to each of the prior sessions. Then we can be sure we’ll hit the ground running.&lt;/p&gt;

&lt;h2 id=&quot;refresh-html5--the-new-javascript-apis&quot;&gt;Refresh: HTML5 &amp;amp; The New JavaScript APIs&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;Previously, this was an embedded iWork document, but it’s been lost to 
history. But it’s very out of date now.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This second session was to go over some of the new HTML5 technologies which are at our disposal. It worked out as a not too bad ~40 minute talk, although, I think I could have gone into more detail in certain areas, especially where using it now is concerned.&lt;/p&gt;

&lt;p&gt;We seem to be on track so far, but the idea is still an issue. Give me a shout if you have any ideas!&lt;/p&gt;

&lt;h2 id=&quot;version-control-with-git&quot;&gt;Version Control with Git&lt;/h2&gt;

&lt;p&gt;The idea of this session was to introduce the concept of version control, but also to introduce &lt;a href=&quot;http://git-scm.com/&quot;&gt;Git&lt;/a&gt; as a tool itself. Instead of relying on a set of slides, the idea of the evening was to explain things at everyones pace.&lt;/p&gt;

&lt;p&gt;As this is not something that’s covered on most people’s degree courses, it’s relevant to all.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://nickcharlton.net/resources/guide_to_git.pdf&quot;&gt;Here is the Guide to Git which I had written beforehand&lt;/a&gt;. It was mostly put together through a series of notes I already had, and then formatted. Some sections could be incorrect, so use with caution, and &lt;a href=&quot;mailto:hello@nickcharlton.net&quot;&gt;complain where necessary&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This session went rather well, with people saying nice things about it afterwards. At the &lt;a href=&quot;http://nickcharlton.net/post/the-digital-peninsulas-first-web-unconference&quot;&gt;Unconference&lt;/a&gt;, &lt;a href=&quot;http://www.fuel-communications.co.uk/&quot;&gt;Ben Masters of Fuel&lt;/a&gt; said that it had been useful to him. Because of this, I will likely turn it into it’s own blog post soon.&lt;/p&gt;

&lt;h2 id=&quot;getting-started-with-python&quot;&gt;Getting Started with Python&lt;/h2&gt;

&lt;p&gt;This session was intended to be a basic introduction to &lt;a href=&quot;http://python.org/&quot;&gt;Python&lt;/a&gt;. It’s the language I intend to use in projects for &lt;a href=&quot;http://termisoc.org/&quot;&gt;TermiSoc&lt;/a&gt; from this point onwards. In the session, I talked a little about the language itself (thus the slides), and then moved on to the guide I had prepared.&lt;/p&gt;

&lt;p&gt;You will find the slides below and the &lt;a href=&quot;http://nickcharlton.net/resources/getting_started_with_python.pdf&quot;&gt;guide linked here&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Previously, this was an embedded iWork document, but it’s been lost to 
history. But it’s very out of date now.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The next few sessions intend to expand upon where we started here, going over some of the nice build in data structures, using libraries and moving on to some web application stuff. I also intend to start talking about what we’d like to build.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: Please use any of the PDFs or slides linked above and modify them; but please don’t sell them, and make sure that you link back. If you want a more formal license, it’s &lt;a href=&quot;http://creativecommons.org/licenses/by-nc-sa/2.0/uk/&quot;&gt;CC BY-NC-SA&lt;/a&gt;. Also; please give me a shout if you’re using them somewhere, I’d love to know how they can be improved.&lt;/em&gt;&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>An Ultra-simple Guide to Reading XML in Java, using SAX</title>
        <link href="https://nickcharlton.net/posts/guide-to-sax-in-java.html" />
        <id>https://nickcharlton.net/posts/guide-to-sax-in-java.html</id>
        <published>Sun, 06 Feb 2011 00:00:00 +0000</published>
        <updated>Sun, 06 Feb 2011 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;For the piece of work I have been dealing with recently, I was required to implement persistence using XML in Java. I figured this would be simple. Java and XML are used all the time, right? Should be easy.&lt;/p&gt;

&lt;p&gt;However, after reading various bits of writing on the subject, from &lt;a href=&quot;http://www.cafeconleche.org/books/xmljava/chapters/ch05.html&quot;&gt;Chapter 5 of Java and XML&lt;/a&gt; to this O’Reilly OnJava article on &lt;a href=&quot;http://onjava.com/pub/a/onjava/2002/06/26/xml.html&quot;&gt;Simple XML Parsing with SAX and DOM&lt;/a&gt;, as suggested by &lt;a href=&quot;http://www.chrisbunney.com/&quot;&gt;Chris&lt;/a&gt;, it still didn’t cut the ultra simplicity I wanted to Just Get the Damn Thing Done™.&lt;/p&gt;

&lt;h2 id=&quot;sample-xml-file&quot;&gt;Sample XML File&lt;/h2&gt;

&lt;p&gt;For this example, I’m just going to show you how to deal with a single element inside an XML document. Obviously, in the real world, it wouldn’t be this simple, but it should be enough to provide the understanding you need.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;
&amp;lt;people&amp;gt;
	&amp;lt;person&amp;gt;
		&amp;lt;age&amp;gt;50&amp;lt;/age&amp;gt;
	&amp;lt;/person&amp;gt;
&amp;lt;/people&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;opening-the-file&quot;&gt;Opening the File&lt;/h2&gt;

&lt;p&gt;The block below opens up the file, parses it’s contents, then closes it back up. It does this by opening up the file in a stream, then passing this stream into the SAX parser.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import java.io.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;

public class SAXClient {
    public static void main(String[] args) {
        try {
            // specify the SAXParser
            XMLReader parser = XMLReaderFactory.createXMLReader(
                &quot;com.sun.org.apache.xerces.internal.parsers.SAXParser&quot;
            );
            // setup the handler
            ContentHandler handler = new Handler();
            parser.setContentHandler(handler);
            // open the file
            FileInputStream in = new FileInputStream(&quot;file.xml&quot;);
            InputSource source = new InputSource(in);
            // parse the data
            parser.parse(source);
            // print an empty line under the data
            System.out.println();
            // close the file
            in.close();
        }
        catch (Exception e) {
            System.err.println(e); 
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;handling-the-content&quot;&gt;Handling the Content&lt;/h2&gt;

&lt;p&gt;We implement a class which extends the DefaultHandler, which handles what happens when it reaches each part of the XML document.&lt;/p&gt;

&lt;p&gt;When the handler reaches the start of the element, a flag is set to true. When it reaches the end of the tag, this flag is set to false. When it is inside the tag, the contents is printed out.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;

public class Handler extends DefaultHandler {
    private boolean inAge = false;

    public void startElement(String namespaceURI, String localName, String qualifiedName, Attributes atts) throws SAXException {
        if (localName.equals(&quot;age&quot;)) inAge = true;
    }

    public void endElement(String namespaceURI, String localName, String qualifiedName) throws SAXException {
        if (localName.equals(&quot;age&quot;)) inAge = false;
    }

    public void characters(char[] ch, int start, int length) throws SAXException {
        if (inAge) {
            for (int i = start; i &amp;lt; start+length; i++) {
                System.out.print(ch[i]); 
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To deal with the output, you’ll need to implement some form of data structure to hold the data. A good tip that can be provided here is that the parser will follow from the top to the bottom when navigating your XML structure. This can be taken advantage of when dealing with the pending data.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>ProgComp: A Programming Competitions Blog</title>
        <link href="https://nickcharlton.net/posts/progcomp-a-programming-competitions-blog.html" />
        <id>https://nickcharlton.net/posts/progcomp-a-programming-competitions-blog.html</id>
        <published>Sun, 26 Dec 2010 00:00:00 +0000</published>
        <updated>Sun, 26 Dec 2010 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Last week I launched &lt;a href=&quot;http://progcomp.tumblr.com/&quot;&gt;ProgComp&lt;/a&gt;. The idea is to produce a continuing flow of curated programming competitions.&lt;/p&gt;

&lt;p&gt;I think competitions are a great way to stretch peoples’ minds on problems they would not otherwise encounter. There are lots of things to learn across many different fields in Computer Science, from data handling algorithms to neural networks in AI and Physics in games.&lt;/p&gt;

&lt;p&gt;The hope is to over time produce a site where you can find open and available competitions, covering a variety of different subject areas.&lt;/p&gt;

&lt;p&gt;Obviously, I cannot do this alone, so, if you hear of any upcoming competitions, please &lt;a href=&quot;http://progcomp.tumblr.com/submit&quot;&gt;submit it here&lt;/a&gt;.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>BCS Lecture Series: Physical Security in IT</title>
        <link href="https://nickcharlton.net/posts/bcs-lecture-series-physical-security-in-it.html" />
        <id>https://nickcharlton.net/posts/bcs-lecture-series-physical-security-in-it.html</id>
        <published>Wed, 10 Nov 2010 00:00:00 +0000</published>
        <updated>Wed, 10 Nov 2010 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;This month’s lecture was delivered by &lt;a href=&quot;http://www.hackner-security.com/&quot;&gt;Thomas Hackner&lt;/a&gt;, from the &lt;a href=&quot;http://www.fh-ooe.at/en/&quot;&gt;University of Applied Sciences, Hagenberg, Upper Austria&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It was certainly an interesting talk, he covered the various standards (ISO 27001:2005), and pointed out a few case studies, and points often over looked. This was then followed up with a demonstration, and “practical” showing lock picking.&lt;/p&gt;

&lt;h2 id=&quot;points&quot;&gt;Points&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Electronic door access systems often just control the door latch, rather than the deadbolt itself.
    &lt;ul&gt;
      &lt;li&gt;this means that typically, it is possible to circumvent the latch using a piece of plastic. Most systems will also not log door openings, leaving such entrances undetected.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Entry systems that use PIN code systems should be changed regularly, otherwise it will eventually be obvious to a casual visitor what the code is.&lt;/li&gt;
  &lt;li&gt;There is usually a simple to identify weakest link in a Physical system, much like any other system.
    &lt;ul&gt;
      &lt;li&gt;an example given was an industrial fridge in student accommodation in Austria. To open the whole fridge it was secured by a much simpler lock than the individual compartments.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;People generally disclose more information over an internal phone system.
    &lt;ul&gt;
      &lt;li&gt;this is most likely because they assume you are trusted for using it.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;In a report in 2008, the Financial Services Authority found that 10/39 small and large companies in the financial industry had basic lapses of security.
    &lt;ul&gt;
      &lt;li&gt;in most cases they had implemented solutions like CCTV, and PIN code access on doors, but simply left those doors open, for example.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Other examples highlighted oversights such as:
    &lt;ul&gt;
      &lt;li&gt;access to server rooms with visitor passes&lt;/li&gt;
      &lt;li&gt;keypad entry systems, but leaving main doors open&lt;/li&gt;
      &lt;li&gt;in companies where they had been implemented, 8/10 of the employees questioned had no idea, or only a cursory understanding of a clear desk policy.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Another example given was of an &lt;a href=&quot;http://www.securitypitfalls.org/2010/02/security-is-no-matte.html&quot;&gt;Airport in Rome which had no security overnight&lt;/a&gt;. This meant that someone was able to walk through the airport, potentially planting banned objects.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;in-conclusion&quot;&gt;In Conclusion&lt;/h2&gt;

&lt;p&gt;Overall, it was a good talk. The examples showed where people often overlook potential security policies, and the relevance of physical penetration testing. On display where &lt;a href=&quot;ttp://www.amazon.co.uk/gp/product/1597496111?ie=UTF8&amp;amp;tag=nisbl-21&amp;amp;linkCode=as2&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=1597496111&quot;&gt;Practical Lock Picking: A Physical Penetration Tester’s Training Guide&lt;/a&gt; and &lt;a href=&quot;http://www.amazon.co.uk/gp/product/0470747617?ie=UTF8&amp;amp;tag=nisbl-21&amp;amp;linkCode=as2&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=0470747617&quot;&gt;Unauthorised Access: Physical Penetration Testing for IT Security Teams&lt;/a&gt; which should hopefully stir on my initial interest further.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>BCS Lecture Series: Apple (The Birth of a Third Platform)</title>
        <link href="https://nickcharlton.net/posts/bcs-lecture-series-apple-the-birth-of-a-third-platform.html" />
        <id>https://nickcharlton.net/posts/bcs-lecture-series-apple-the-birth-of-a-third-platform.html</id>
        <published>Wed, 29 Sep 2010 00:00:00 +0000</published>
        <updated>Wed, 29 Sep 2010 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Yesterday evening, &lt;a href=&quot;http://twitter.com/thisisthechris&quot;&gt;Chris Hunt&lt;/a&gt; and myself went along to a talk at the BCS from Apple, entitled “The Birth of a Third Platform, Mobile Computing in HE”. Overall, it was a good talk, even with the heavy Apple marketing angle.&lt;/p&gt;

&lt;p&gt;The talk was hosted by Lawrence Stevenson, who I assume holds a job title like “HE Evangelist”, or such like.&lt;/p&gt;

&lt;p&gt;The majority of the talk focused on covering the usage of mobile technologies in HE. From mobile optimised web apps which would tell you about the status of washing machine in halls (&lt;a href=&quot;http://housing.uiowa.edu/departments/facilities/laundry.htm&quot;&gt;such as at the University of Iowa&lt;/a&gt;), to iPad’s in class being used by students to find out information during lectures.&lt;/p&gt;

&lt;h2 id=&quot;figures&quot;&gt;Figures&lt;/h2&gt;

&lt;p&gt;Some interesting figures were given out during the presentation.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Since 2009, mobile device shipments have declined 10% year on year.&lt;/li&gt;
  &lt;li&gt;But, smartphones grew 20%, and now account for 17% of mobile devices.&lt;/li&gt;
  &lt;li&gt;Smartphones are projected to take 37% of the market by 2012.&lt;/li&gt;
  &lt;li&gt;In US HE, smartphone use has gone from 1.28% in 2005, to 51.2% in 2009.&lt;/li&gt;
  &lt;li&gt;Graduate skill sets have changed from a dominance on physical skills to skills relating to IT (intellectual). In the last 50 years, this has changed from less than 40% to over 65%, today.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;other-points&quot;&gt;Other Points&lt;/h2&gt;

&lt;p&gt;Some other points made were:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“Mobile computing is about producing context and engagement”.&lt;/li&gt;
  &lt;li&gt;“We’ve gone from what is your mobile strategy to where is your mobile strategy”.&lt;/li&gt;
  &lt;li&gt;Slowness in HE integration can route back to lecturers not wanting their jobs to change. Links to Darwin were made on that point.&lt;/li&gt;
  &lt;li&gt;There isn’t an education discount for the iPad because the demand is so high.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;in-conclusion&quot;&gt;In Conclusion&lt;/h2&gt;

&lt;p&gt;Unfortunately, towards the end we didn’t get a chance to catch him and ask a few questions. He was swamped with people asking about iOS SDK related things, those which are &lt;a href=&quot;http://www.apple.com/hotnews/thoughts-on-flash/&quot;&gt;widely&lt;/a&gt; &lt;a href=&quot;http://daringfireball.net/2010/09/app_store_guidelines&quot;&gt;reported&lt;/a&gt; too.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>On Long Way Down</title>
        <link href="https://nickcharlton.net/posts/on-long-way-down.html" />
        <id>https://nickcharlton.net/posts/on-long-way-down.html</id>
        <published>Fri, 17 Sep 2010 00:00:00 +0000</published>
        <updated>Fri, 17 Sep 2010 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;a href=&quot;http://www.amazon.co.uk/gp/product/0751538957?ie=UTF8&amp;amp;tag=nisbl-21&amp;amp;linkCode=as2&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=0751538957&quot;&gt;Long Way Down&lt;/a&gt; was a good series. Back in 2007, Ewan McGregor and Charley Boorman set out on a motorcycle trip from John O’Groats to Cape Town. I just finished reading the accompanying book, that’s been on my book shelf for quite a while.&lt;/p&gt;

&lt;p&gt;All in all, it was a good book. It’s laid out as a dialogue between the two, and in general split into chapters at country borders.&lt;/p&gt;

&lt;p&gt;I think it is a book where, through the eyes of the authors, you get an idea of what travelling through many different places is like. For me at least; it’s spurred me on to other books, like &lt;a href=&quot;http://www.amazon.co.uk/gp/product/0140054103?ie=UTF8&amp;amp;tag=nisbl-21&amp;amp;linkCode=as2&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=0140054103&quot;&gt;Ted Simon’s Jupiter’s Travels&lt;/a&gt;, the story of another adventure on a Motorbike.&lt;/p&gt;

&lt;p&gt;In the process of &lt;a href=&quot;http://twitter.com/nickcharlton/status/24594581601&quot;&gt;tweeting about it&lt;/a&gt;, I did the (probably stupid) thing of reading the reviews from Amazon. All-in-all, they were pretty awful. Most of them were centred around them being well known elsewhere, or the ghost writing. Others on the convoy they travelled with.&lt;/p&gt;

&lt;p&gt;I can certainly agree with some of it, the travelling does feel a little bit over done for what they talked about in both the series and the book. Mostly, the book replays the series, not adding much to the original story.&lt;/p&gt;

&lt;p&gt;I think this is a shame. Whilst some come would come to a book to hear about the adventure, I hoped it would come with the hope that it would give a far greater depth. There’s no answer to the ‘Why?’. Why did they pick this route? Why the specific bikes? What about the choice of the rest of the equipment? None of these were answered.&lt;/p&gt;

&lt;p&gt;Another point made was the focus on the timetable. Whilst it’s quite easy to plan traveling around somewhere like Europe, travelling through Africa is unpredictable - almost entirely because of the road surfaces. Setting up commitments to meet with Aid Agencies, or ferries seems to me to have been a mistake. Though; I suspect some of this comes from their status, and the filming.&lt;/p&gt;

&lt;p&gt;All of these things aside though, it’s a good read. I think it’s a good springboard onto other books, like Jupiter’s Travels, or classics like &lt;a href=&quot;http://www.amazon.co.uk/gp/product/0140095144?ie=UTF8&amp;amp;tag=nisbl-21&amp;amp;linkCode=as2&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=0140095144&quot;&gt;Thesiger’s Arabian Sands&lt;/a&gt; (which I must read).&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>CSS3 Bundle</title>
        <link href="https://nickcharlton.net/posts/css3-bundle.html" />
        <id>https://nickcharlton.net/posts/css3-bundle.html</id>
        <published>Sat, 10 Jul 2010 00:00:00 +0000</published>
        <updated>Sat, 10 Jul 2010 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;The default CSS bundle in TextMate doesn’t include any of the new features of CSS3. I’d tried other TextMate CSS3 plugins, but in general they didn’t cover enough and seemed unmaintained.&lt;/p&gt;

&lt;p&gt;So I made this.&lt;/p&gt;

&lt;h2 id=&quot;features&quot;&gt;Features&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;border-radius&lt;/li&gt;
  &lt;li&gt;box-shadow&lt;/li&gt;
  &lt;li&gt;Odd/Even Selectors&lt;/li&gt;
  &lt;li&gt;Columns (WebKit only)&lt;/li&gt;
  &lt;li&gt;Text Stroke (WebKit only)&lt;/li&gt;
  &lt;li&gt;text-shadow&lt;/li&gt;
  &lt;li&gt;opacity&lt;/li&gt;
  &lt;li&gt;display-inline&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;where-to-find-it&quot;&gt;Where to Find it&lt;/h2&gt;

&lt;p&gt;You can find &lt;a href=&quot;http://github.com/nickcharlton/CSS3-Bundle&quot; title=&quot;nickcharlton&apos;s CSS3-Bundle at master - GitHub&quot;&gt;the source on GitHub&lt;/a&gt;, &lt;a href=&quot;http://github.com/downloads/nickcharlton/CSS3-Bundle/CSS3.tmbundle&quot; title=&quot;&amp;quot;). And [the issues page here](http://github.com/nickcharlton/CSS3-Bundle/issues &amp;quot;Issues - nickcharlton/CSS3-Bundle - GitHub&quot;&gt;a download here&lt;/a&gt;, where you can suggest new features.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Contributions are very welcome, it’s certainly not complete.&lt;/em&gt;&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>New Project</title>
        <link href="https://nickcharlton.net/posts/new-project.html" />
        <id>https://nickcharlton.net/posts/new-project.html</id>
        <published>Sun, 30 May 2010 00:00:00 +0000</published>
        <updated>Sun, 30 May 2010 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;After a few weeks of work, this is “Version 4”, of my blog. I have a slightly different design, and a different focus which should represent changes in me.&lt;/p&gt;

&lt;p&gt;I do have a like of redoing some aspect of this every 6 months. I think it’s a cross between me changing as a person, and my desire to have this as a reflection of that.&lt;/p&gt;

&lt;p&gt;This is a little app written in &lt;a href=&quot;http://www.sinatrarb.com/&quot; title=&quot;Sinatra&quot;&gt;Sinatra&lt;/a&gt;, utilising &lt;a href=&quot;http://daringfireball.net/projects/markdown/&quot; title=&quot;Daring Fireball: Markdown&quot;&gt;Markdown&lt;/a&gt; for the markup (handled by &lt;a href=&quot;http://github.com/nex3/maruku&quot; title=&quot;nex3&apos;s maruku at master - GitHub&quot;&gt;Maruku&lt;/a&gt;), backed up by MySQL. It’s hosted on Apache/Passenger at &lt;a href=&quot;http://prgmr.com/xen/&quot; title=&quot;Linux and NetBSD Xen VPS hosting.&quot;&gt;Prgmr.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The code is also on &lt;a href=&quot;http://github.com/nickcharlton/nickcharlton.net&quot; title=&quot;nickcharlton&apos;s nickcharlton.net at master - GitHub&quot;&gt;GitHub&lt;/a&gt;. Including most of the content.&lt;/p&gt;

&lt;h2 id=&quot;plans&quot;&gt;Plans&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Open Source the underlying “blogging engine”, as a project called “Sinba”.&lt;/li&gt;
  &lt;li&gt;Use it to dump notes as I work on projects.&lt;/li&gt;
  &lt;li&gt;Document a few things released, or nearing release.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;future&quot;&gt;Future&lt;/h2&gt;

&lt;p&gt;Over the Summer I have (through my Dad) scored a two month Sysadmin Job at ADNEC, in Abu Dhabi. Around this time last year, I spent two days doing some work experience. Which, apart from being brilliant (there’s nowhere you can find a better mix of people than abroad) and educational, was very useful to me.&lt;/p&gt;

&lt;p&gt;For the evenings, I have a few things rolled up my sleeve. As usual, there’s a lot of things I’d like to learn, from iPhone OS Programming, to some C stuff. But, mostly, I want to go into September with at least a few simple projects up my sleeve, and something bid nearly there.&lt;/p&gt;

&lt;p&gt;Ready? Let’s Begin.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>SQLite, ADO.NET &amp; CSharp</title>
        <link href="https://nickcharlton.net/posts/sqlite-with-csharp.html" />
        <id>https://nickcharlton.net/posts/sqlite-with-csharp.html</id>
        <published>Wed, 14 Apr 2010 00:00:00 +0000</published>
        <updated>Wed, 14 Apr 2010 00:00:00 +0000</updated>
        <summary type="html">&lt;h3 id=&quot;introduction&quot;&gt;Introduction&lt;/h3&gt;

&lt;p&gt;For a project I wanted to use SQLite for dealing with it’s data. I quite readily found &lt;a href=&quot;http://sqlite.phxsoftware.com/&quot; title=&quot;System.Data.SQLite&quot;&gt;System.Data.SQLite&lt;/a&gt;. But, then I needed to figure out how ADO.NET worked. This has resulted in these short notes, mostly compiled from O’Reilly’s out of print, &lt;a href=&quot;http://oreilly.com/catalog/9780596003616/&quot; title=&quot;ADO.NET in a Nutshell - O&apos;Reilly Media&quot;&gt;ADO.NET in a Nutshell&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Most of the rest of these notes are general to ADO.NET, but with a few System.Data.SQLite specifics.&lt;/p&gt;

&lt;h3 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;You’ll need the System.Data.SQLite.dll file.&lt;/li&gt;
  &lt;li&gt;You’ll need to reference it in the VS Solution.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;You’ll need to include the namespace at the top of your code.&lt;/p&gt;

    &lt;p&gt;using System.Data.SQLite;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;connection-string&quot;&gt;Connection String&lt;/h3&gt;

&lt;p&gt;The first stage is to connect to the DB and to do that you’ll create a “Connection” using a ConnectionString. An example is below:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// initialise the database
SQLiteConnection con = new SQLiteConnection(&quot;Data Source=../../Database.sqlite&quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;opening&quot;&gt;Opening&lt;/h3&gt;

&lt;p&gt;This simply opens the database connection. (See Closing for, ..closing).&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;con.Open;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can test the status of the connection by writing:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Console.WriteLine(&quot;Connection is &quot; + con.State.ToString());
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;queries-and-nonqueries&quot;&gt;Queries and NonQueries&lt;/h3&gt;

&lt;p&gt;ADO.NET differentiates between “Queries” and “NonQueries”. The difference is dependant on the returned data.&lt;/p&gt;

&lt;p&gt;A “NonQuery” is an SQL statement such as “UPDATE”, “DELETE” and “INSERT”, as they do not return data. You will however get a count of how many rows were effected on execution.&lt;/p&gt;

&lt;h3 id=&quot;command-string&quot;&gt;Command String&lt;/h3&gt;

&lt;h4 id=&quot;nonqueries&quot;&gt;NonQueries&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// define our SQL.
string SQL = &quot;UPDATE people SET name=&apos;Someone Else&apos; WHERE id=1&quot;;

// Create the Command
SQLiteCommand cmd = new SQLiteCommand(SQL, con);

// Open the connection (if you haven&apos;t already).
con.Open();

// execute our command.
int rowsAffected = cmd.ExecuteNonQuery();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;First we define our SQL query (which is just a string), then instantiate the SQLiteCommand object, and finally run our command.&lt;/p&gt;

&lt;h4 id=&quot;returning-a-single-result&quot;&gt;Returning a Single Result&lt;/h4&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExecuteScalar()&lt;/code&gt; method returns a single value. This would be used to return the result of a calculation. Such as requesting a COUNT.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cmd.ExecuteScalar();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The returned value is an Object of the result.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;string SQL = &quot;SELECT id FROM people WHERE id=&apos;1&apos;;&quot;;

SQLiteCommand cmd = new SQLiteCommand(SQL, con);

con.Open();

object result = cmd.ExecuteScalar();
int convert = Convert.ToInt16(result);

con.Close();
Console.WriteLine(convert.ToString() + &quot; rows.&quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;creating-tables&quot;&gt;Creating Tables&lt;/h4&gt;

&lt;p&gt;Creating tables is done in a similar way to the above, but with just a different SQL command. (So, I won’t mention it here.)&lt;/p&gt;

&lt;h4 id=&quot;performing-selects--using-datareader&quot;&gt;Performing SELECTs / Using DataReader&lt;/h4&gt;

&lt;p&gt;The DataReader class provides the methods to iterate over rows in a database. So, this is the function to use to perform SELECT statements that return more than one record.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;string SQL = &quot;SELECT ContactName FROM Customers&quot;;

// Create ADO.NET objects.
SQLiteConnection con = new SQLiteConnection(&quot;Data Source=../../Database.sqlite&quot;);
SQLiteCommand cmd = new SqlCommand(SQL, con);
SQLiteDataReader reader = null;

// Execute the command.
try
{
    con.Open();
    reader = cmd.ExecuteReader();

    // Iterate over the results.
    while (reader.Read())
    {
        lstNames.Items.Add(reader[&quot;ContactName&quot;]);
    }
}
catch (Exception err)
{
    MessageBox.Show(err.ToString());
}
finally
{
    if (reader != null) reader.Close();
    con.Close();
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can also run more than one SELECT query, split with a semicolon, like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;string SQL = &quot;SELECT * FROM Customers; SELECT * FROM Orders;&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To differentiate between the two result sets, you need to apply a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reader.NextResult();&lt;/code&gt;, like below:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;while (reader.Read())
{
    // (Process the category rows here.)
}

reader.NextResult();

while (reader.Read())
{
    // (Process the product rows here.)
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Note: Most of these code examples are from Chapter/Section 5.2.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;This has hopefully provided you a basic introduction to using the System.Data.SQLite and ADO.NET. Once you understand how ADO.NET works, System.Data.SQLite drops straight in. It follows all of the usual ADO.NET conventions.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Fixing Missing Gem Problems on OS X</title>
        <link href="https://nickcharlton.net/posts/fixing-missing-gem-problems.html" />
        <id>https://nickcharlton.net/posts/fixing-missing-gem-problems.html</id>
        <published>Fri, 26 Feb 2010 00:00:00 +0000</published>
        <updated>Fri, 26 Feb 2010 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;em&gt;Disclaimer: This could potentially bugger up Ruby and Ruby Gems on your machine, proceed carefully.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Recently I’ve been doing quite a few &lt;a href=&quot;http://en.wikipedia.org/wiki/REST&quot; title=&quot;Representational State Transfer - Wikipedia, the free encyclopedia&quot;&gt;REST&lt;/a&gt; API building stuffs with &lt;a href=&quot;http://www.ruby-lang.org/en/&quot; title=&quot;Ruby Programming Language&quot;&gt;Ruby&lt;/a&gt;, &lt;a href=&quot;http://www.sinatrarb.com/&quot; title=&quot;Sinatra&quot;&gt;Sinatra&lt;/a&gt; and trying to pick up &lt;a href=&quot;http://ar.rubyonrails.org/&quot; title=&quot;Active Record -- Object-relation mapping put on rails&quot;&gt;ActiveRecord&lt;/a&gt; to use with Rails. I was however getting quite a lot of problems with Gems loading, but not loading completely.&lt;/p&gt;

&lt;p&gt;If when running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem check --alien&lt;/code&gt;, you get something similar to below:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rails-1.2.6 has 2 problems
	/Library/Ruby/Gems/1.8/specifications/rails-1.2.6.gemspec:
	Spec file doesn&apos;t exist for installed gem

	/Library/Ruby/Gems/1.8/cache/rails-1.2.6.gem:
	missing gem file /Library/Ruby/Gems/1.8/cache/rails-1.2.6.gem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Clear out all of the gems located in the following directories. You will need to similarly remove anything in the bin/, cache/, doc/, gems/ and specifications/ directories.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/Library/Ruby/Gems/1.8
/Users/&amp;lt;username&amp;gt;/.gem/ruby/1.8
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(These directories can be found by entering &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem environment&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;You will then need to reinstall all of your gems. You may wish to issue a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem check&lt;/code&gt; on each of them to ensure it’s all good.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Thoughts on the iPad</title>
        <link href="https://nickcharlton.net/posts/thoughts-ipad.html" />
        <id>https://nickcharlton.net/posts/thoughts-ipad.html</id>
        <published>Wed, 27 Jan 2010 00:00:00 +0000</published>
        <updated>Wed, 27 Jan 2010 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Tablets and small computers have always interested me. My iPhone is a great little mobile computer - the internet in the palm of your hand, and the rest of that..&lt;/p&gt;

&lt;p&gt;The thing is, I’ve never seen the actual &lt;em&gt;point&lt;/em&gt; of tablets. They are generally not convenient enough to make them worthwhile, the wrong size, too limited and have an awful input mechanism.&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;There was an [interesting discussion on Hacker News](http://news.ycombinator.com/item?id=1077772 “Hacker News&lt;/td&gt;
      &lt;td&gt;The Healthcare System: An Apple Tablet’s Biggest Opportunity”) earlier today about tablets in medicine. The article itself suggested that tablets would be a great way for doctors to enter information about patients - forgetting of course that paper has worked wonderfully since pre-Nightingale and such an adaption would be an uphill struggle. Anyway, I digress.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;From &lt;a href=&quot;https://nickcharlton.net/new-macbook-pro&quot; title=&quot;nickcharlton.net/blog&quot;&gt;my rather biased perspective&lt;/a&gt;, I think Apple has done something interesting with the 
“iPad”.&lt;/p&gt;

&lt;p&gt;The interesting bit here, at least, I think, is their showing off of iWork on the device. The biggest issue with such a device is text input. Being just a large screen, there’s not the ability to easily enter text. The iPhone isn’t a bad, if a little small, but still a pretty good mobile keypad, the iPad is similar, although on a larger scale.&lt;/p&gt;

&lt;p&gt;What I can’t see though is the gap between the iPhone and the MacBook models. Therefore, I don’t think it’s revolutionary, at all.&lt;/p&gt;

&lt;p&gt;I find reading to be quite possible on both my iPhone and MacBook Pro, leading me to not need something to fit in that gap. At least, not with just a LCD screen. e-Ink would be something different entirely, reading of an LCD screen is tiring on the eyes.&lt;/p&gt;

&lt;p&gt;I can’t see why you would use such a device for writing a document when a laptop is far nicer and why you would use such a device for watching TV, recorded or otherwise.&lt;/p&gt;

&lt;p&gt;What I do think is clever though is the price point. It’s not cheap enough that I would buy one - not least at the 3G end of the scale, but priced cleverly enough to provide another option for people looking for a new machine.&lt;/p&gt;

&lt;p&gt;Maybe the revolutionary aspect is producing a excellent, portable device?&lt;/p&gt;

&lt;p&gt;I intend to see it pan out, I can’t see me buying one, but it’s interesting, none-the-less.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Ideas and University</title>
        <link href="https://nickcharlton.net/posts/ideas-and-university.html" />
        <id>https://nickcharlton.net/posts/ideas-and-university.html</id>
        <published>Tue, 03 Nov 2009 00:00:00 +0000</published>
        <updated>Tue, 03 Nov 2009 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;It’s been a while since I’ve written down a set of thoughts. Most of which occurred before I started University. What I do want to ponder here I will come to in a moment, but now I’ve been at University for nearly 4 months - I’m trailing on to the end of the first term.&lt;/p&gt;

&lt;p&gt;It has certainly been a experience, but something I thought would be overly more strenuous and complex. Especially after the first few weeks. Unfortunately, it has not been like what I had hoped. I certainly don’t find the amount of work I’m given hard enough - or even to the level of other’s at different Universities and the topics covered never seem to go into the depth I’d like. All in all, it either feels like I’m not learning anything or that I am rather having to teach myself.&lt;/p&gt;

&lt;p&gt;Arguably this is a failing of the University itself and speaking with &lt;a href=&quot;http://bma.subvert.org.uk/&quot; title=&quot;~bma — Home&quot;&gt;others&lt;/a&gt; the first year doesn’t appear to change, and &lt;a href=&quot;http://twitter.com/rossbearman&quot;&gt;some&lt;/a&gt; have stated that the second year is not much better.&lt;/p&gt;

&lt;p&gt;What I do want to talk about though are ideas. Ideas are those amazing things you dream up doing and in most people’s cases, don’t get the chance to execute them. I used to have a constant flow of such things. Possible projects flying out of my ears just waiting to be executed.&lt;/p&gt;

&lt;p&gt;Since getting to Plymouth though, that all seems to have changed. I have ended up having passing possibles, rather than something that sticks for any reasonable amount of time.&lt;/p&gt;

&lt;p&gt;Quite possibly this down to not specifically having any problems to solve, or indeed too many options to solve it, neither of them which are worth spending time on.&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;As an example, I have been meaning to launch something of substance on [Heroku](http://heroku.com/ “Heroku&lt;/td&gt;
      &lt;td&gt;Ruby Cloud Platform as a Service”) for a while. Could do with patching up my Ruby knowledge by writing some client-side apps, rather than simple ideas executed with Sinatra and then abandoned. Similarly, I haven’t used C for a while, would like to learn some Objective-C for Mac development (and Cocoa, obviously) but have no reasons to push ahead with it. No project idea generally means that I merely read and plan about either a project, or something new (like a framework) and not end up with something at the end.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;As part of my course, I am supposed to be learning C#, half of my problem here is that I don’t use Windows and therefore I don’t have a need to build anything there. Although, fortunately I seem to be able to pick  it up quite easily.&lt;/p&gt;

&lt;p&gt;So, instead of spending my time working on something specific I am slowly reading through a mountain of books. To run off a list off the top of my head:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.amazon.co.uk/gp/product/0596529864?ie=UTF8&amp;amp;tag=nisbl-21&amp;amp;linkCode=as2&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=0596529864&quot; title=&quot;Learning Ruby: Amazon.co.uk: Michael Fitzgerald: Books&quot;&gt;Learning Ruby&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.amazon.co.uk/gp/product/0596102097?ie=UTF8&amp;amp;tag=nisbl-21&amp;amp;linkCode=as2&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=0596102097&quot; title=&quot;Learning C# 2005, Second Edition: Amazon.co.uk: Jesse Liberty, Brian MacDonald: Books&quot;&gt;Learning C# 2005&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.amazon.co.uk/gp/product/0470147628?ie=UTF8&amp;amp;tag=nisbl-21&amp;amp;linkCode=as2&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=0470147628&quot; title=&quot;Beginning Linux Programming: Amazon.co.uk: Neil Matthew, Richard Stones: Books&quot;&gt;Beginning Linux Programming&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.amazon.co.uk/gp/product/0596529260?ie=UTF8&amp;amp;tag=nisbl-21&amp;amp;linkCode=as2&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=0596529260&quot; title=&quot;RESTful Web Services: Amazon.co.uk: Leonard Richardson, Sam Ruby: Books&quot;&gt;RESTful Web Services&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;ttp://www.amazon.co.uk/gp/product/1430218096?ie=UTF8&amp;amp;tag=nisbl-21&amp;amp;linkCode=as2&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=1430218096&quot;&gt;Learn C on the Mac&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;and finally: &lt;a href=&quot;http://www.amazon.co.uk/gp/product/0321503619?ie=UTF8&amp;amp;tag=nisbl-21&amp;amp;linkCode=as2&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=0321503619&quot; title=&quot;Cocoa Programming for Mac OS X: Amazon.co.uk: Aaron Hillegass: Books&quot;&gt;Cocoa Programming for Mac OS X&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I do also have a couple of UK-centric business books which I’m slowly attacking, although there is not much point (bar the background reading) to learn about that when I have nothing to execute!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;mailto:nickcharlton91@gmail.com&quot;&gt;You can of course put an idea on a postcard.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course this “lack of ideas” could instead be covering another problem. Instead I may have lost the passion I used to have for simple things that felt worthwhile building. Whether that is a good or bad thing, I’m not sure. Possibly better in the sense that I end up not starting and then stopping projects, but not so great in the sense that in four months I have produced nothing.&lt;/p&gt;

&lt;p&gt;Retrospect is a wonderful thing and the last few weeks can be looked back upon with the odd test, only three assignments, and a pretty dodgy sleep pattern for me to look back upon. Don’t get me wrong, University is worth it’s while, however when you feel you are getting nowhere (because it’s too simple), it’s easy to wonder if being somewhere is worth it’s time.&lt;/p&gt;

&lt;p&gt;So now, my general week consists of 5/6 lecture slots, sinking my way through some quite heavy books, eventually learning the Guitar and spending just a little too much money. That’s quite a far cry from the last two years. Maybe I shouldn’t complain?&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Jekyll and GitHub</title>
        <link href="https://nickcharlton.net/posts/jekyll-and-github.html" />
        <id>https://nickcharlton.net/posts/jekyll-and-github.html</id>
        <published>Mon, 05 Oct 2009 00:00:00 +0000</published>
        <updated>Mon, 05 Oct 2009 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;After quite a bit of work, I have finally moved over to using GitHub pages, Jekyll and Markdown to power &lt;a href=&quot;http://nickcharlton.net&quot; title=&quot;nickcharlton.net&quot;&gt;nickcharlton.net&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From now on, this is a Git repository made up of Markdown files and a sprinkling of HTML. This comes with quite a few benefits, those of which I hope to explain below.&lt;/p&gt;

&lt;h3 id=&quot;versioning--backup&quot;&gt;Versioning &amp;amp; Backup&lt;/h3&gt;

&lt;p&gt;As it is powered primarily by Git, each post has version tracking without any extra effort. This was recently added to Wordpress, instead of a clean solution, this just turned to make the database even more of a mess than it already was.&lt;/p&gt;

&lt;p&gt;Secondly, due to the distributed nature of Git, I always have two copies. One is served up at GitHub, and the other is my working copy stored locally on my main machine. On top of this, other people can fork it, adding more backups.&lt;/p&gt;

&lt;h3 id=&quot;security--access&quot;&gt;Security &amp;amp; Access&lt;/h3&gt;

&lt;p&gt;Without the intention of using this primarily as a vehicle for moaning about Wordpress, this is another of it’s issues. Certainly the two most important issues are security and access to the content.&lt;/p&gt;

&lt;p&gt;As is typical with any large dynamic project, it’s going to have security issues. This means that new versions of Wordpress are routinely rolled out, this means that you need to keep up-to-date with the latest build to keep your writings safe.&lt;/p&gt;

&lt;p&gt;Next, is access of the posts themselves. They’re stored in a MySQL database. This both means that accessing the individual posts requires accessing the database, rather than just opening a file. Opening a flat HTML file will always scale better than a solution which involves reloading the same information from a database on each connection (presuming no caching is used).&lt;/p&gt;

&lt;h3 id=&quot;the-source&quot;&gt;The Source&lt;/h3&gt;

&lt;p&gt;You can find the source on &lt;a href=&quot;http://github.com/nickcharlton/nickcharlton.github.com&quot;&gt;GitHub&lt;/a&gt;. Please feel free to reuse any parts which are not posts without attribution. The posts themselves are licensed under a Creative Commons Attribution-Non-Commercial-Share Alike license. You can read more about that in the &lt;a href=&quot;http://github.com/nickcharlton/nickcharlton.github.com/blob/master/README.markdown&quot;&gt;README&lt;/a&gt;.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>SSH Public Key Screencast Notes</title>
        <link href="https://nickcharlton.net/posts/ssh-public-key-screencast-notes.html" />
        <id>https://nickcharlton.net/posts/ssh-public-key-screencast-notes.html</id>
        <published>Fri, 18 Sep 2009 00:00:00 +0000</published>
        <updated>Fri, 18 Sep 2009 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;em&gt;These are just a few quick notes to accompany &lt;a href=&quot;http://vimeo.com/6523718&quot;&gt;Peter Upfold’s screencast&lt;/a&gt;. If you haven’t ready I would suggest you give it a watch before following this rather brief notes&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;on-the-local-machine&quot;&gt;On the local machine&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;run: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh-keygen -t rsa&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Accept default path.&lt;/li&gt;
  &lt;li&gt;Enter a passphrase. (terminal can save this in Keychain)&lt;/li&gt;
  &lt;li&gt;Finder &amp;gt; Go &amp;gt; Type: “.ssh”&lt;/li&gt;
  &lt;li&gt;Copy &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id_rsa.pub&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;on-the-remote-machine&quot;&gt;On the remote machine&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;run: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;touch .ssh/authorized_keys&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Edit the file: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ssh/authorised_keys&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Paste the contents of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id_rsa.pub&lt;/code&gt; file into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ssh/authorized_keys&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;permissions-check&quot;&gt;Permissions Check&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;authorized_keys&lt;/code&gt; file needs to be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rw&lt;/code&gt; for the user.&lt;/li&gt;
  &lt;li&gt;That is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod 600&lt;/code&gt; to change, if needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;logging-in&quot;&gt;Logging in&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Login as usual from the Terminal.&lt;/li&gt;
  &lt;li&gt;When asked for the password to the ssh key, this is the passphrase mentioned earlier.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Once again, thank you, &lt;a href=&quot;http://peter.upfold.org.uk&quot;&gt;Peter&lt;/a&gt; for recording it for me, and the mention.&lt;/em&gt;&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>New MacBook Pro</title>
        <link href="https://nickcharlton.net/posts/new-macbook-pro.html" />
        <id>https://nickcharlton.net/posts/new-macbook-pro.html</id>
        <published>Sat, 12 Sep 2009 00:00:00 +0000</published>
        <updated>Sat, 12 Sep 2009 00:00:00 +0000</updated>
        <summary type="html">&lt;h3 id=&quot;intro&quot;&gt;Intro&lt;/h3&gt;

&lt;p&gt;Yesterday my new MacBook Pro arrived. It’s a 2.66Ghz model with a Matte screen. It really is wonderful. It’s certainly the fastest, and most custom machine I’ve had. Coupled with the &lt;a href=&quot;http://nickio.posterous.com/a-few-notes-on-the-apple-education-store&quot; title=&quot;A Few Notes on the Apple Education Store - Nick Charlton&quot;&gt;Apple Student Discount&lt;/a&gt;, it does end up working affordable.&lt;/p&gt;

&lt;p&gt;Last night I tested out the power of this machine. There’s various things which I couldn’t do which I now can. Today &lt;a href=&quot;http://nickio.posterous.com/macbook-pro-some-photos&quot; title=&quot;MacBook Pro: Some Photos - Nick Charlton&quot;&gt;I took some photos&lt;/a&gt; (ones of the Matte screen are rather thin on the ground).&lt;/p&gt;

&lt;h3 id=&quot;flash&quot;&gt;Flash&lt;/h3&gt;

&lt;p&gt;I can play Flash. Especially HD flash, I rather liked watching the HD shorts on Vimeo but this had to come to a halt a while back as I wasn’t able to use it. It got to the point where I couldn’t even use YouTube, so it’s quite a difference.&lt;/p&gt;

&lt;p&gt;Last night I watched some iPlayer content in HD - the difference in it not stuttering is quite different.&lt;/p&gt;

&lt;p&gt;Interestingly; Flash in Snow Leopard (could have also been before) is now a separate process. So Flash itself uses ~170% CPU, whilst Safari uses &amp;lt;10%.&lt;/p&gt;

&lt;h3 id=&quot;development&quot;&gt;Development&lt;/h3&gt;

&lt;p&gt;Development should be faster. I was having lag of minutes at times using TextMate, especially as I typically develop server side using ExpanDrive to aid with using SFTP. This also meant quite a few things have fallen behind where I’d like them, or I haven’t put the effort in where I should. This really was a reflection of my main development environment rather than me, a pain, but now that bottleneck is gone.&lt;/p&gt;

&lt;p&gt;So now, hopefully various things will be given a kick up the backside into getting started, finished or otherwise.&lt;/p&gt;

&lt;h3 id=&quot;in-conclusion&quot;&gt;In Conclusion&lt;/h3&gt;

&lt;p&gt;All in all, everything is great. Things are slotting together ready for my move over to Plymouth for University. So if you’re around, pop me an email and visit!&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Public Key Auth Screencast</title>
        <link href="https://nickcharlton.net/posts/public-key-auth-screencast.html" />
        <id>https://nickcharlton.net/posts/public-key-auth-screencast.html</id>
        <published>Fri, 11 Sep 2009 00:00:00 +0000</published>
        <updated>Fri, 11 Sep 2009 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;After a request by &lt;a href=&quot;http://twitter.com/nickcharlton/status/3884874880&quot;&gt;Peter Upfold&lt;/a&gt; on Twitter for ideas for a screencast, I suggested he do something on Public Key Authentication. My wish was made true as he made it up yesterday.&lt;/p&gt;

&lt;div style=&quot;padding:56.25% 0 0 0;position:relative;&quot;&gt;&lt;iframe src=&quot;https://player.vimeo.com/video/6523718&quot; style=&quot;position:absolute;top:0;left:0;width:100%;height:100%;&quot; frameborder=&quot;0&quot; webkitallowfullscreen=&quot;&quot; mozallowfullscreen=&quot;&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;script src=&quot;https://player.vimeo.com/api/player.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;&lt;a href=&quot;https://vimeo.com/6523718&quot;&gt;Set Up Public Key Authentication for SSH 
on the Mac&lt;/a&gt; from &lt;a href=&quot;https://vimeo.com/peteru&quot;&gt;Peter Upfold&lt;/a&gt; on &lt;a href=&quot;https://vimeo.com&quot;&gt;Vimeo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I was rather surprised to hear myself mentioned, so a huge hat tip (presumes a huge hat, I guess…) to Peter for doing that.&lt;/p&gt;

&lt;p&gt;Thanks!&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Configuring Sudo on Debian</title>
        <link href="https://nickcharlton.net/posts/configuring-sudo-on-debian.html" />
        <id>https://nickcharlton.net/posts/configuring-sudo-on-debian.html</id>
        <published>Fri, 26 Jun 2009 00:00:00 +0000</published>
        <updated>Fri, 26 Jun 2009 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Setting up sudo on Debian can seem daunting at first, but the process is really quite simple.&lt;/p&gt;

&lt;p&gt;To do this you must use the command “visudo”. Whilst you do not need to use “vi/vim” to do the actual editing (as it will use your preferred editor as listed in .bashrc), you will not be able to save changes.&lt;/p&gt;

&lt;p&gt;From here you will see the config file. Under “# User alias specification” you will want to list the users required to access. You can comma separate values here.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# User alias specification
User_Alias STAFF nickcharlton, otheruser
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, although optional, it is possible to specify the applications that the user can run.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Cmnd alias specification
Cmnd_Alias DEB = /usr/bin/apt-get
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Similarly, this section can be comma separated. On a system where it’s users can be trusted at a higher level, it’s not important to drill down tightly on these.&lt;/p&gt;

&lt;p&gt;Next, you need to allow access under the “# User privilege specification”. Where no commands have been specified it is appropriate to simply duplicate that of the root user.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# User privilege specification
root ALL=(ALL) ALL
MAINTAINERS ALL = DEB
STAFF ALL-(ALL) ALL
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And that’s it. Another simple task, which at first can seem a little daunting.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sources: &lt;a href=&quot;http://newbiedoc.berlios.de/wiki/How_to_configure_Sudo_to_run_programs_as_a_different_user&quot;&gt;NewbieDoc: Configuring Sudo to Run Programs as Another User&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Configuring an SSH banner on Debian</title>
        <link href="https://nickcharlton.net/posts/ssh-banner-debian.html" />
        <id>https://nickcharlton.net/posts/ssh-banner-debian.html</id>
        <published>Wed, 24 Jun 2009 00:00:00 +0000</published>
        <updated>Wed, 24 Jun 2009 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; I’ve since come up with something much better, which dynamically generates
the content. You can see it in the post: &lt;a href=&quot;/posts/debian-ubuntu-dynamic-motd.html&quot;&gt;Debian/Ubuntu: Dynamic MOTD&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Configuring a “Welcome Banner” is a great way to notify your users about the 
machine they are about to login to. I personally use this to inform the user of the 
IP, Hostname, OS version and someone to contact.&lt;/p&gt;

&lt;p&gt;Edit the file under “/etc/motd”.&lt;/p&gt;

&lt;p&gt;Change this to something informative such as the below:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;========================================
=         Welcome to Hitchcock         =
=  IP: 10.10.10.10                     =
=  Hostname: hitchcock.example.com     =
=  OS: Debian 5.01/Lenny               =
========================================
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Just a short one this, but useful to know.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Introduction to SQLite 2 with PHP 5</title>
        <link href="https://nickcharlton.net/posts/introduction-to-sqlite-2-with-php-5.html" />
        <id>https://nickcharlton.net/posts/introduction-to-sqlite-2-with-php-5.html</id>
        <published>Mon, 18 May 2009 00:00:00 +0000</published>
        <updated>Mon, 18 May 2009 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;Where a full &lt;acronym title=&quot;Relational Database Management System&quot;&gt;RDBMS&lt;/acronym&gt; is unnecessary, &lt;a href=&quot;http://www.sqlite.org/&quot;&gt;SQLite&lt;/a&gt; provides the perfect stand in. However, most articles are based around the Object-Orientated way of dealing with SQLite in PHP. This article provides an explanation of how to use it procedurally.&lt;/p&gt;

&lt;h3 id=&quot;points&quot;&gt;Points&lt;/h3&gt;

&lt;p&gt;A couple of things to note before going ahead and using SQLite are that:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The directory and the database need to be writable from the web server.&amp;lt;/li&amp;gt;&lt;/li&gt;
  &lt;li&gt;As it is simply a flatfile, this should be stored where it cannot be accessed by browsers.&lt;/li&gt;
  &lt;li&gt;Where an “auto_increment” field is used in MySQL, it should be created by specifying “INTEGER PRIMARY KEY” when configuring the table. This is further explained later.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;opening-a-sqlite-db&quot;&gt;Opening a SQLite DB&lt;/h3&gt;

&lt;p&gt;Opening an SQLite Database is quite simple. The “sqlite_open” function is assigned to a variable which is then used to indicate that database later on in queries. If the database does not already exist then it will be created upon opening.&lt;/p&gt;

&lt;p&gt;The file extension need not be .db, it may be anything, or even have none. The directory which is referenced should be both outside of the web root and writable by the server user. On Debian Etch with Apache, the user is “www-data”.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$db = sqlite_open(&quot;../db/name.db&quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;creating-a-table&quot;&gt;Creating a table&lt;/h3&gt;

&lt;p&gt;Before data can be written or read to a Database, a table needs to be created to hold it. The the line below runs a CREATE query which describes the table which will be created. Whilst I do not wish to explain how to use SQL in this article, the following creates a table with two fields, one (called “id”) which automatically increments in regards to it’s value and is set as a primary key and the second, called “name” containing text of up to 255 characters. The table itself is called “example”. The &lt;a href=&quot;http://www.sqlite.org/lang.html&quot;&gt;SQLite Documentation&lt;/a&gt; provides a good guide behind the syntax.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sqlite_query($db, &quot;CREATE TABLE example (id INTEGER PRIMARY KEY, name CHAR(255))&quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;inserting-data&quot;&gt;Inserting data&lt;/h3&gt;

&lt;p&gt;To insert data, the “sqlite_query” function is used which specifies the database in the first part, and then the query behind it.&lt;/p&gt;

&lt;p&gt;The query in this example inserts the name “Nick” into the field “name” in the table “example”. As the field “id” auto increments, it is not necessary to specify a value for it.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sqlite_query($db, &quot;INSERT INTO example (name) VALUES (&apos;Nick&apos;)&quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;querying-data&quot;&gt;Querying data&lt;/h3&gt;

&lt;p&gt;The following line would fetch all of the data in the table “example” and display it as a “printed array”.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$result = sqlite_query($db, &quot;SELECT * FROM table&quot;);
while ($row = sqlite_fetch_array($result)) {
	echo &quot;&amp;lt;pre&amp;gt;&quot;;
	print_r($row);
	echo &quot;&amp;lt;/pre&amp;gt;&quot;;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On an alternative note, such a method can be used to see the contents of a query and check the names of fields, at least in development.&lt;/p&gt;

&lt;h3 id=&quot;further-reading&quot;&gt;Further Reading&lt;/h3&gt;

&lt;p&gt;Whilst this article focuses on the procedural method built into PHP 5 (which incidentally is limited to SQLite 2), SQLite can also be accessed through &lt;acronym title=&quot;PHP Data Objects&quot;&gt;PDO&lt;/acronym&gt; and through the Object Orientated Method.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://php.net/sqlite&quot;&gt;PHP Manual: SQLite&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.devshed.com/c/a/PHP/Introduction-to-Using-SQLite-with-PHP-5/&quot;&gt;Object Orientated SQLite, Devshed&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://devzone.zend.com/article/863&quot;&gt;SQLite 3 with PDO, Zend DevZone&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Installing Ubuntu 9.04 on an SD card</title>
        <link href="https://nickcharlton.net/posts/ubuntu-on-sdcard.html" />
        <id>https://nickcharlton.net/posts/ubuntu-on-sdcard.html</id>
        <published>Fri, 08 May 2009 00:00:00 +0000</published>
        <updated>Fri, 08 May 2009 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;&lt;strong&gt;Note: This article is quite old. It probably doesn’t apply anymore. Your
mileage may vary.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To to this I used a &lt;a href=&quot;https://www.amazon.co.uk/gp/product/B000WQKOQM?ie=UTF8&amp;amp;tag=nisbl-21&amp;amp;linkCode=as2&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=B000WQKOQM&quot;&gt;SanDisk 4GB SDHC card which can be bought for around £6/$9&lt;/a&gt;. Better performance could be gained from using a faster card. However, for the most part this card is quite acceptable.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: Solid state memory (such as SD cards) generally has a limited amount of writes that can be possibly made to it. This means that the card used will not last forever.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;Step 1&lt;/h2&gt;

&lt;p&gt;The first step is to prepare the tools you need and boot from the installation media.&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;Ubuntu 9.04 Desktop ISO image&lt;/li&gt;
	&lt;li&gt;A CD or thumb drive (to install the image from)&lt;/li&gt;
	&lt;li&gt;SanDisk 4GB SDHC Card&lt;/li&gt;
	&lt;li&gt;A machine to try it on&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Step 2&lt;/h2&gt;

&lt;p&gt;I chose to first boot into the live environment and run the installer from there. Whilst I have had bad experiences using CD drives for such a procedure, booting off a thumb drive is quite acceptable.&lt;/p&gt;

&lt;p&gt;Next, start the install.&lt;/p&gt;

&lt;h2&gt;Step 3&lt;/h2&gt;

&lt;p&gt;When you reach the partitioning stage you will need to select the SD card, rather than the hard drive. On my system (an HP (2133) Mini) this appeared as &quot;/dev/sdc&quot; as a SCSI device.&lt;/p&gt;

&lt;p&gt;The naming of the device will vary per system, so could show up as hdc (if it is on an IDE controller) or in another manner.&lt;/p&gt;

&lt;h2&gt;Finally&lt;/h2&gt;

&lt;p&gt;Once that is finished all that is required is to boot the system. You could set this in the bios to boot first, in which case it&apos;d boot if an external drive is inserted first, or pick at boot time.&lt;/p&gt;

&lt;h2&gt;Some Notes&lt;/h2&gt;

&lt;ul&gt;
	&lt;li&gt;Overall, Ubuntu 9.04 used 2GB (or 50%) of my 2GB drive.&lt;/li&gt;
	&lt;li&gt;Issues: When suspending I noticed that it would not be able to mount the disk, and thus fail.&lt;/li&gt;
&lt;/ul&gt;

</summary>
    </entry>
    
    <entry>
        <title>Using &apos;ab&apos;, ApacheBench to test Web Server Performance</title>
        <link href="https://nickcharlton.net/posts/using-apachebench.html" />
        <id>https://nickcharlton.net/posts/using-apachebench.html</id>
        <published>Sun, 05 Apr 2009 00:00:00 +0000</published>
        <updated>Sun, 05 Apr 2009 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;ApacheBench (referred to as &apos;ab&apos; in the terminal) is a tool for testing web server performance by allowing you to test how long a set of requests per second the web server is capable of serving. &lt;/p&gt;

&lt;p&gt;Whilst this does not reflect a model of real world usage, it can aid in the tweaking and performance improvement of the Web Server itself.&lt;/p&gt;

&lt;p&gt;This tool is of course aimed at testing Apache servers, however it can be used on others.&lt;/p&gt;

&lt;h2&gt;Usage&lt;/h2&gt;

&lt;p&gt;Usage is rather simple. At it&apos;s core, the amount of requests to complete and the url to test are required. On top of this you may inform the application to carry out more than one request at a time, as show in the second example.&lt;/p&gt;

&lt;pre lang=&quot;Bash&quot;&gt;
   $ ab -n 100 http://domain/

   $ ab -n 100  -c 3 http://domain/
&lt;/pre&gt;

&lt;p&gt;When this is run the specified web page is downloaded and the time taken for it to happen is measured.&lt;/p&gt;

&lt;h2&gt;Some Results&lt;/h2&gt;

&lt;p&gt;These are the results I gained from running this against the server behind this, testing using 3 concurrent connections and 100 requests.&lt;/p&gt;

&lt;pre lang=&quot;Bash&quot;&gt;
$ ab -n 100 -c 3 http://nickcharlton.net/
This is ApacheBench, Version 2.3 &amp;lt;$Revision: 655654 $&amp;gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking nickcharlton.net (be patient).....done


Server Software:        Apache/2.2.3
Server Hostname:        nickcharlton.net
Server Port:            80

Document Path:          /
Document Length:        34350 bytes

Concurrency Level:      3
Time taken for tests:   13.371 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      3471900 bytes
HTML transferred:       3435000 bytes
Requests per second:    7.48 [#/sec] (mean)
Time per request:       401.120 [ms] (mean)
Time per request:       133.707 [ms] (mean, across all concurrent requests)
Transfer rate:          253.58 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       27  135 160.8     87    1295
Processing:    65  257 562.0    140    5108
Waiting:       31  149 169.3    105    1297
Total:        104  392 617.4    254    5483

Percentage of the requests served within a certain time (ms)
  50%    254
  66%    317
  75%    348
  80%    510
  90%    776
  95%    920
  98%   2311
  99%   5483
 100%   5483 (longest request)
&lt;/pre&gt;

&lt;h2&gt;Limitations&lt;/h2&gt;

&lt;p&gt;ApacheBench does not however give you a figure to suggest how many requests a server may complete, or reflect the usage which will be shown with a real set of users. This is because any given page or application may consist of many requests&lt;/p&gt;

&lt;h2&gt;What to do with the Results?&lt;/h2&gt;

&lt;p&gt;The results which you gain are specifically useful in tweaking settings regarding the web server itself. Whilst ApacheBench cannot directly tell you what needs tweaking, once you start changing settings you can realise what is best for your server.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;My thanks go out to &lt;a href=&quot;http://blog.init.hr/&quot;&gt;Ante&lt;/a&gt; for helping me improve this post, clearing up mistakes and ensuring that the right information was being given. This post has been slightly edited since it&apos;s original posting.&lt;/em&gt;&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>DNS Testing Tools</title>
        <link href="https://nickcharlton.net/posts/dns-testing-tools.html" />
        <id>https://nickcharlton.net/posts/dns-testing-tools.html</id>
        <published>Wed, 25 Feb 2009 00:00:00 +0000</published>
        <updated>Wed, 25 Feb 2009 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;When working with DNS, it can be a painful experience. From the time it takes for the root servers to update with your new records to simple mistakes made when working with large zone files. Here I explain six tools which can help route out DNS problems using the terminal. All of these work out of the box on OSX. &lt;/p&gt;

&lt;h2&gt;nslookup&lt;/h2&gt;

&lt;p&gt;nslookup is used to pull up the basic information associated with a domain.  Using nslookup is as simple as providing the following arguments:&lt;/p&gt;

&lt;pre lang=&quot;Bash&quot;&gt;$ nslookup nickcharlton.org.uk&lt;/pre&gt;

&lt;p&gt;A likely response could be similar to the following: &lt;/p&gt;

&lt;pre lang=&quot;Bash&quot;&gt;Server:		208.67.222.222
Address:	208.67.222.222#53

Non-authoritative answer:
Name:	nickcharlton.org.uk
Address: 78.86.198.208&lt;/pre&gt;

&lt;p&gt;This response tells you the nameserver which was queried, in this case one of those of OpenDNS which I use at home and the response from the name server when it was queried. The last IP is that of my server.&lt;/p&gt;

&lt;h2&gt;dig&lt;/h2&gt;

&lt;p&gt;Dig is a most useful tool, it officially stands for Domain Information Gopher and allows you to pull the data from the name server just as your machine would in taking a request. This allows you to pull the public records for the domain.&lt;/p&gt;

&lt;pre lang=&quot;Bash&quot;&gt;$ dig nickcharlton.org.uk&lt;/pre&gt;

&lt;pre lang=&quot;Bash&quot;&gt;
&amp;lt;&amp;lt;&amp;gt;&amp;gt; DiG 9.4.2-P2 &amp;lt;&amp;lt;&amp;gt;&amp;gt; nickcharlton.org.uk
;; global options:  printcmd
;; Got answer:
;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, status: NOERROR, id: 4635
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;nickcharlton.org.uk.		IN	A

;; ANSWER SECTION:
nickcharlton.org.uk.	603718	IN	A	78.86.198.208

;; Query time: 19 msec
;; SERVER: 208.67.222.222#53(208.67.222.222)
;; WHEN: Sat Feb 21 03:49:25 2009
;; MSG SIZE  rcvd: 53
	
&lt;/pre&gt;

&lt;p&gt;What can be taken from here is the A record for the domain. That is the record which takes a domain and points it to the IP of the remote machine.&lt;/p&gt;

&lt;h2&gt;dig (with @IP/Domain)&lt;/h2&gt;

&lt;p&gt;Whilst not strictly a tool within it&apos;s own right, it is different from it&apos;s basic no arguments call. This allows you to pull records from a specific server, which is useful for testing the records from a new server before it is live. An example is provided below, and would be used if pulling records from a local server:&lt;/p&gt;

&lt;pre lang=&quot;Bash&quot;&gt;$ dig @localhost nickcharlton.org.uk&lt;/pre&gt;

&lt;p&gt;And it&apos;s response:&lt;/p&gt;

&lt;pre lang=&quot;Bash&quot;&gt;
; &amp;lt;&amp;lt;&amp;gt;&amp;gt; DiG 9.4.2-P2 &amp;lt;&amp;lt;&amp;gt;&amp;gt; @kubrick.nickcharlton.org.uk nickcharlton.org.uk
; (1 server found)
;; global options:  printcmd
;; Got answer:
;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, status: NOERROR, id: 63435
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 2

;; QUESTION SECTION:
;nickcharlton.org.uk.		IN	A

;; ANSWER SECTION:
nickcharlton.org.uk.	604800	IN	A	78.86.198.208

;; AUTHORITY SECTION:
nickcharlton.org.uk.	604800	IN	NS	ns1.nickcharlton.org.uk.
nickcharlton.org.uk.	604800	IN	NS	ns2.nickcharlton.org.uk.
;; ADDITIONAL SECTION:
ns1.nickcharlton.org.uk. 10800	IN	A	92.243.13.80
ns2.nickcharlton.org.uk. 10800	IN	A	78.86.198.208
;; Query time: 27 msec
;; SERVER: 92.243.13.80#53(92.243.13.80)
;; WHEN: Sat Feb 21 03:50:44 2009
;; MSG SIZE  rcvd: 121
&lt;/pre&gt;

&lt;p&gt;Note the increase in the details of the records. I&apos;m not entirely sure why this happens, so if you could enlighten me, please do. (Also note that the appropriate call I used was towards my server directly, and not local host, as I don&apos;t have a DNS server here.)&lt;/p&gt;

&lt;h2&gt;rndc&lt;/h2&gt;

&lt;p&gt;This tool allows you to deal with your current DNS settings. The most commonly use argument of this is &quot;reload&quot; and that allows you to flush, or reload your DNS settings so that they are all fresh.&lt;/p&gt;

&lt;pre lang=&quot;Bash&quot;&gt;$ rndc reload&lt;/pre&gt;

&lt;h2&gt;host&lt;/h2&gt;

&lt;p&gt;Host translates IP&apos;s from domains and vice versa. This is what happens when you query a domain, you get an IP response. It can also be used backwards.&lt;/p&gt;

&lt;pre lang=&quot;Bash&quot;&gt;$ host nickcharlton.org.uk&lt;/pre&gt;

&lt;pre lang=&quot;Bash&quot;&gt;
nickcharlton.org.uk has address 78.86.198.208
nickcharlton.org.uk mail is handled by 20 fb.mail.gandi.net.
nickcharlton.org.uk mail is handled by 10 spool.mail.gandi.net.
&lt;/pre&gt;

&lt;h2&gt;whois&lt;/h2&gt;

&lt;p&gt;Last there is whois. This is obviously the most well known of the lot and provides ownership information for the domain. I will not provide an example here, as it will be very long, but this is simply used as below:&lt;/p&gt;

&lt;pre lang=&quot;Bash&quot;&gt;$ whois nickcharlton.org.uk&lt;/pre&gt;

</summary>
    </entry>
    
    <entry>
        <title>Converting a sparseimage to a dmg</title>
        <link href="https://nickcharlton.net/posts/converting-a-sparseimage-to-a-dmg.html" />
        <id>https://nickcharlton.net/posts/converting-a-sparseimage-to-a-dmg.html</id>
        <published>Sat, 01 Nov 2008 00:00:00 +0000</published>
        <updated>Sat, 01 Nov 2008 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;The process of converting from one type of disk image to another is usually handled by the software created by it. However on the other hand, if you don’t have enough HD space when say, running a full Carbon Copy Cloner backup of your machine, you may elect to instead just produce an uncompressed sparseimage.&lt;/p&gt;

&lt;p&gt;The process for doing this is quite easy and logical, but not so easy to remember.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;hdiutil convert -format UDZO Source.sparseimage -o Output.dmg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The process will of course take quite a while, but you will be provided with a simple “..” style progress bar.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hdutil&lt;/code&gt; is also SMP aware, so it can use more than one CPU. This not only faster, but will also hammer your machine. With this reason in mind, I’m not sure if it would be wise to run it with a low process number, if you are planning on doing a lot at the same time, maybe this would be a good idea.&lt;/p&gt;

</summary>
    </entry>
    
    <entry>
        <title>Setting Up lm-sensors on Debian Etch</title>
        <link href="https://nickcharlton.net/posts/setting-up-lm-sensors-on-debian-etch.html" />
        <id>https://nickcharlton.net/posts/setting-up-lm-sensors-on-debian-etch.html</id>
        <published>Tue, 24 Jun 2008 00:00:00 +0000</published>
        <updated>Tue, 24 Jun 2008 00:00:00 +0000</updated>
        <summary type="html">&lt;p&gt;lm-sensors is a package which provides temperature monitoring under Linux. This guide explains how to setup lm-sensors under Debian Etch.&lt;/p&gt;

&lt;p&gt;To install it under Debian, use:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apt-get install lm-sensors
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Although there are other pre-requisites, a default install of Etch will work quite nicely.&lt;/p&gt;

&lt;p&gt;Next, you need to load the i2c-dev module. This allows you to access some of the chips on your motherboard which are hooked up to temperature sensors.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;modprobe i2c-dev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now you need to detect the sensors on your system using the wizard in the following command. I just stack to the defaults.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sensors-detect
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At the end of this you will be told a list of devices, these need to be loaded too, as an example:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;modprobe -a i2c-viapro i2c-isa eeprom w83627hf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By loading the:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sensors
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;app you will be thrown at with a list of devices, it will also tell you some voltages, however for me the CPU temp, fan sped, and system temperature.&lt;/p&gt;

&lt;p&gt;Hard Disk temperature can also be watched, this is slightly easier however as you simply need to install hddtemp and specify the drives.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apt-get install hddtemp

hddtemp /dev/hd?
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;if your system sees your drives as SCSI, or you have SCSI drives that is…&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Produced in aid of: &lt;a href=&quot;http://www.debian-administration.org/articles/327&quot;&gt;http://www.debian-administration.org/articles/327&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</summary>
    </entry>
    
</feed>
