<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Blaz Merela]]></title><description><![CDATA[A problem solver. Working on infrastructure and more.]]></description><link>https://merela.org</link><generator>GatsbyJS</generator><lastBuildDate>Thu, 15 Aug 2024 12:42:05 GMT</lastBuildDate><item><title><![CDATA[Rails boot sequence]]></title><link>https://merela.org/blog/rails_boot</link><guid isPermaLink="false">https://merela.org/blog/rails_boot</guid><pubDate>Thu, 19 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;As I was having issues with a misbehaving gem, I needed to demystify Rails’ boot sequence for myself. As a (brief) web search also didn’t return many visual resources, I made a summary of &lt;a href=&quot;https://oersi.org/resources/aHR0cHM6Ly9hdi50aWIuZXUvbWVkaWEvMzE1Njg=&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;Xavier Noria’s talk&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.142857142857146%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABZElEQVQoz12SXc+CMAyF9/9/lfcKd8YLgxDwg6BRQJAPlZqneftm0KTptq7nnHZz8mfTNGms61qqqpL7/a7Onvh4PPT8er1K0zRyu93UbU3u9XqJMzADbNtWxnGU8/ms/n6/9XLXdRrjONZCiHDOkySRsiy13i0V9n2vrMMwKLj58/nUSA5CO1/ecz4Y8fv9aoQZVjuzPKoAZQwoZg2ZYcwAacUUUMjcaJkCzjHW5JgbeSMwHOfPD0A8TVM5nU5SFIVcLhdtBcUYkTZtxpBbTgF9hSRgpFXYUcEaR5kpZM9r2+vOFH4+n39ALqAMZiKFKKZtm6OZr8qfsaMoz3PZ7Xay2Wxku91qmyiEDHYrAMQIqGEk/Ar/6zkKUBNFkSrLskyVUgigAbBer9eyWq00Ho9H/ZPcnQHCEoahBEGgvt/vZ69qCjCAeQSibzYy/TYwm5Og1cPhoMAoYASsAVqC2J/1Ff4AVCSdZlGrl/gAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/a8dd4150f9fa6dd414135fd4c2fc5376/c54d4/rails_boot.webp 175w,
/static/a8dd4150f9fa6dd414135fd4c2fc5376/a3432/rails_boot.webp 350w,
/static/a8dd4150f9fa6dd414135fd4c2fc5376/426ac/rails_boot.webp 700w,
/static/a8dd4150f9fa6dd414135fd4c2fc5376/c139f/rails_boot.webp 1050w,
/static/a8dd4150f9fa6dd414135fd4c2fc5376/7f403/rails_boot.webp 1400w,
/static/a8dd4150f9fa6dd414135fd4c2fc5376/2cfbe/rails_boot.webp 2004w&quot;
              sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/a8dd4150f9fa6dd414135fd4c2fc5376/4edbd/rails_boot.png 175w,
/static/a8dd4150f9fa6dd414135fd4c2fc5376/13ae7/rails_boot.png 350w,
/static/a8dd4150f9fa6dd414135fd4c2fc5376/8c557/rails_boot.png 700w,
/static/a8dd4150f9fa6dd414135fd4c2fc5376/e996b/rails_boot.png 1050w,
/static/a8dd4150f9fa6dd414135fd4c2fc5376/2cefc/rails_boot.png 1400w,
/static/a8dd4150f9fa6dd414135fd4c2fc5376/813c1/rails_boot.png 2004w&quot;
            sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/a8dd4150f9fa6dd414135fd4c2fc5376/8c557/rails_boot.png&quot;
            alt=&quot;Rails boot process&quot;
            title=&quot;Rails boot process&quot;
            loading=&quot;lazy&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To summarize in words:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Define load paths for deps (Bundle.setup)&lt;/li&gt;
&lt;li&gt;Load rails/all (railties of Rails components)&lt;/li&gt;
&lt;li&gt;Load gem deps (Bundle.require)&lt;/li&gt;
&lt;li&gt;Define the &lt;code class=&quot;language-text&quot;&gt;Application&lt;/code&gt; class&lt;/li&gt;
&lt;li&gt;Define assorted paths (autoload etc.)  #Rails.application.initialize!&lt;/li&gt;
&lt;li&gt;Load config/environments/#{Rails.env}.rb&lt;/li&gt;
&lt;li&gt;Load initializers in config/initializers&lt;/li&gt;
&lt;li&gt;Eager load application (if nil). Happens by default in &lt;code class=&quot;language-text&quot;&gt;production&lt;/code&gt; mode.&lt;/li&gt;
&lt;li&gt;Load &lt;code class=&quot;language-text&quot;&gt;config/routes.rb&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Run command hooks (if needed).&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item><item><title><![CDATA[Testing IPv6 connectivity on AWS]]></title><link>https://merela.org/blog/testing-ipv6-connectivity-on-aws</link><guid isPermaLink="false">https://merela.org/blog/testing-ipv6-connectivity-on-aws</guid><pubDate>Sat, 05 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Do you need to verify that a particular service handles IPv6 traffic, yet your ISP doesn’t provide you an IPv6 address? This guide will help you work around this by setting up an IPv6-only EC2 and connecting it to the service in question.&lt;/p&gt;
&lt;p&gt;As a rough overview, you’ll need at least these pieces:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;dual-stack VPC&lt;/li&gt;
&lt;li&gt;dual-stack ALB&lt;/li&gt;
&lt;li&gt;A(AAA) record to the ALB&lt;/li&gt;
&lt;li&gt;IPv6-only subnet&lt;/li&gt;
&lt;li&gt;IPv6-only EC2&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We’ll highlight the needed modifications for each, using Terraform. There’re more pieces needed, but the unlisted ones aren’t directly related to IPv6.&lt;/p&gt;
&lt;h2 id=&quot;dual-stack-vpc&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dual-stack-vpc&quot; aria-label=&quot;dual stack vpc permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Dual-stack VPC&lt;/h2&gt;
&lt;p&gt;We start with an already existing VPC. The following configurations have to be added to &lt;a href=&quot;https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws/3.14.0?utm_content=documentLink&amp;#x26;utm_medium=Visual+Studio+Code&amp;#x26;utm_source=terraform-ls&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;the VPC module&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token property&quot;&gt;enable_ipv6&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
assign_ipv6_address_on_creation
private_subnet_assign_ipv6_address_on_creation
public_subnet_ipv6_prefixes
private_subnet_ipv6_prefixes
database_subnet_ipv6_prefixes&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;enable_ipv6&lt;/code&gt; lets Amazon provide you an IPv6 CIDR block, in which case you cannot set on its prefix length nor IP range. (You may alternatively &lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-byoip.html&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;bring your own IP&lt;/a&gt;.)&lt;/p&gt;
&lt;h2 id=&quot;ipv6-only-subnet&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ipv6-only-subnet&quot; aria-label=&quot;ipv6 only subnet permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;IPv6-only subnet&lt;/h2&gt;
&lt;p&gt;The subnet’s definition is straightforward:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_subnet&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;vpc_id&lt;/span&gt;                                         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.vpc_id
  &lt;span class=&quot;token property&quot;&gt;ipv6_cidr_block&lt;/span&gt;                                &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.cidr
  &lt;span class=&quot;token property&quot;&gt;ipv6_native&lt;/span&gt;                                    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;assign_ipv6_address_on_creation&lt;/span&gt;                &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;enable_resource_name_dns_aaaa_record_on_launch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here’s how you might set the above &lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt;s. (We’re using Terragrunt, thence the &lt;code class=&quot;language-text&quot;&gt;dependency&lt;/code&gt; keyword.)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;locals&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# Fixed when using AWS-provided IPv6 CIDR block.&lt;/span&gt;
&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;cidr_prefix_length_vpc&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;56&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;cidr_prefix_length_subnet&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;64&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;count_existing_subnets&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;vpc_id&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; dependency.vpc.outputs.id
&lt;span class=&quot;token property&quot;&gt;cidr&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; cidrsubnet(
  dependency.vpc.outputs.ipv6_cidr,
  local.cidr_prefix_length_subnet - local.cidr_prefix_length_vpc,
  local.count_existing_subnets + &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Two things to note about the highlighted lines, in order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The CIDR prefix lengths are fixed: at 56 for VPCs and at 64 for subnets. Hence the VPC’s CIDR prefix is extended by &lt;code class=&quot;language-text&quot;&gt;64 - 56&lt;/code&gt; bits above.&lt;/li&gt;
&lt;li&gt;The VPC already had 8 existing subnets and we add the IPv6-only subnet as the 9th one; thus the &lt;code class=&quot;language-text&quot;&gt;9&lt;/code&gt; on the 2nd highlighted line above.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;(Note: outside of this hypothetical scenario, this particular IPv6 subnet would be provisioned from the same configuration as the rest. That is, there wouldn’t be a &lt;code class=&quot;language-text&quot;&gt;+ 1&lt;/code&gt; subnet added in such an ad hoc manner.)&lt;/p&gt;
&lt;h2 id=&quot;alb&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#alb&quot; aria-label=&quot;alb permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ALB&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token property&quot;&gt;ip_address_type&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dualstack&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Related to ALB, note that, as of now, its Target Group &lt;a href=&quot;https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-target-groups.html#target-group-ip-address-type&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;must be&lt;/a&gt; of &lt;code class=&quot;language-text&quot;&gt;target_type = &quot;ip&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;IPv6 target groups only support IP type targets.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;aaaa-record&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#aaaa-record&quot; aria-label=&quot;aaaa record permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;A(AAA) record&lt;/h2&gt;
&lt;p&gt;The AAAA record is required for our IPv6 testing, while the A record is a nice to have, as it’ll make a potential Route53 healthcheck pass. If using Route53, it’s best to use an ‘alias’ extension, letting the AAAA record resolve via ALB’s DNS.&lt;/p&gt;
&lt;h2 id=&quot;ec2&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ec2&quot; aria-label=&quot;ec2 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;EC2&lt;/h2&gt;
&lt;p&gt;Only changes to the &lt;code class=&quot;language-text&quot;&gt;&quot;aws_instance&quot;&lt;/code&gt; resource are&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the addition of &lt;code class=&quot;language-text&quot;&gt;ipv6_address_count&lt;/code&gt;, set to a non-zero value, and&lt;/li&gt;
&lt;li&gt;disablement of &lt;code class=&quot;language-text&quot;&gt;associate_public_ip_address&lt;/code&gt; which presumes an IPv4 address.
Then, as long as you pick a relatively recent &lt;code class=&quot;language-text&quot;&gt;ami&lt;/code&gt;, you should be all set.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In one the dual-stack subnets you’ll also need an EC2 with both IPv4 and IPv6 connectivity.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Finally, to actually test the above setup, you’d SSH into a dual-stack EC2 and from there on SSH further into the IPv6-only EC2. From there you’d then, finally, be able test the IPv6 connectivity of your service in question.&lt;/p&gt;
&lt;p&gt;Note that you could also merely add an IPv6 interface directly to the service’s EC2 and obviate the need of setting up the LB. But since many services find themselves behind an LB, I found it fitting to include ALB setup as well. Especially since its target-groups currently have an IPv6-related limitation.&lt;/p&gt;
&lt;p&gt;We haven’t laid out all the steps in detail, though the above are the important bits needed to test IPv6 in AWS. Reach out if you aren’t able to fill in the missing gaps and I’ll expand on the unclear parts.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Safer terminal]]></title><description><![CDATA[Changing terminal color and other visual feedbacks for safer terminal operations.]]></description><link>https://merela.org/blog/safer-terminal</link><guid isPermaLink="false">https://merela.org/blog/safer-terminal</guid><pubDate>Tue, 19 Jul 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;As much as we strive towards having the whole server fleet be treated as an indistinguishable herd of cattle, there are still times when taking care of pet-servers is necessary. One such time is when the infrastructure is still undergoing a cloud migration, or when a particular server really is a special darling.&lt;/p&gt;
&lt;p&gt;In such cases, any additional feedback to the operator helps in avoiding mistakes. In this post you’ll see approaches that you might use to enable such visual feedback. The end goal is to immediatelly visually discern that one has entered a potentially dangerous environment, one where any mistake might be of grave consequences.&lt;/p&gt;
&lt;p&gt;Most of the time, an operator’s tool of choice is a terminal. We’ll focus on iTerm2, a terminal on macOS. But even if you’re not an iTerm2 user, the following tips might inspire you to apply something similar in your chosen terminal.&lt;/p&gt;
&lt;h2 id=&quot;iterm2&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#iterm2&quot; aria-label=&quot;iterm2 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;iTerm2&lt;/h2&gt;
&lt;h3 id=&quot;triggers&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#triggers&quot; aria-label=&quot;triggers permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Triggers&lt;/h3&gt;
&lt;p&gt;The prerequisite for Triggers is to ‘Install Shell Integration’. You’ll find it in iTerm’s menu bar:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 538px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABzklEQVQoz3XRPW/TQBgHcH8FRr4CKx0oTPA9GNoOVVMBQnRGQqpUEDCDiKAEMbCzMURO4iS2E5LSpoY4du3Eb/diu2lQEAv+c+csdGD43f+ee+5uuFMopdA0Da1WC4ZhQNd11Ot1NJtNNBqNsmeaJlRVheM44JxDnmGMleI4huu6ZU+uK0QMPM2RZjkoS8s5TzMwnpW5kos6FX2OhDBEMRGSVSakXIsJFSkuvMgojnoaNPULjgc6jvodfBOOv3Yx7LXR76pCA+PREEOjCXvUx/Kc4PeC49ecYM4CkJmNnPhYzhmUT1qCzdc2dqsWHtZcPPpwhvuHDu69s0s71TEqbyzsffSxJfZtvJqg8j5ApRaWdg4DbL/1sVmd4vnnCMpuLSyu7sW485IVa/sE1/cT3HxKcO1xghsHFLdfMNx6xrB+QHDlQQhlYyp4l235UO56WHsygzKZ0j9di0L/TovOaSITuqjboxjaSQTDWqXUPY2hS9Y/RG1YSaFbCUYuLZQsZRiY7WJgdnAy7Ik31EuBZ2O5yCDfeHHOVvLLLoSfc4448DA7s4uMEyiUsmBsT/BjbBeeP4Wc2xMHQRiBUAYifu5/5K/KPWEUF7MglPXyL/jWHF5CuDlNAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/8b3734f1eb3482229c1884bdb802d893/c54d4/iterm_tips.webp 175w,
/static/8b3734f1eb3482229c1884bdb802d893/a3432/iterm_tips.webp 350w,
/static/8b3734f1eb3482229c1884bdb802d893/b52d2/iterm_tips.webp 538w&quot;
              sizes=&quot;(max-width: 538px) 100vw, 538px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/8b3734f1eb3482229c1884bdb802d893/4edbd/iterm_tips.png 175w,
/static/8b3734f1eb3482229c1884bdb802d893/13ae7/iterm_tips.png 350w,
/static/8b3734f1eb3482229c1884bdb802d893/9516f/iterm_tips.png 538w&quot;
            sizes=&quot;(max-width: 538px) 100vw, 538px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/8b3734f1eb3482229c1884bdb802d893/9516f/iterm_tips.png&quot;
            alt=&quot;Install Shell Integration&quot;
            title=&quot;Install Shell Integration&quot;
            loading=&quot;lazy&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Then, you can enable &lt;a href=&quot;https://iterm2.com/documentation-automatic-profile-switching.html&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;Automatic Profile Switching&lt;/a&gt;. But, since installing APS on remote hosts is often impractical or inadvisable, you can work around this by creating a so-called Trigger.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.42857142857143%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABYlAAAWJQFJUiTwAAAB7ElEQVQ4y21S2aoiMRDt/1dGQVvbtVftdkU/QF/cV7jz5oOguICgMvfBp7nmTKqm03jlBg6VVConJ6eieWbp6boObNsSpuPDsh1UKh4sy4LrunJe4XmpVEIQBGg2mxwbjQajVqtRFOFaaI7rwXFd4ToOTLsC23HheTIn10RomiYTUrRtm/MKtKY9qpcQ1WoVmtwQdLDq+yIIfBB832cVVByPxxGLxZBKpZDJZJBOp6HrOkc1l3lBe/JSodEtiUQChUKBFdm2E6mjPTpEZATDMLgun89/Qy6X40gCtFAuqyI/FOr1OhOrYvJQPf0dKs+E9O5WqxWZ/ApSmEwmGbpUmjXyMOQFpJSgLlPKqZ4J6TApeoUiVH7p6RR+pXJI61lksxn2U4GIi8Uiq/xG+EpMc9VlQrlcjjpKeRW5oZKDLCOLfiRU/4usmM1mWCwWmE6nWC6X+Pj4zfEV8/kcm80G/X7/5ycrwna7jclkwqQK4/E4yq3Xa6xWKyY8HA4YDodMKEJC8doQ+oedTgfb7ZaLj6cTThLn8xnH4xGXywX3+x232w3X6xWPxwODwSBSKN6bQp50u138+fzE368vvA9BEILxfD45NxqNoMl2i9BsQX9NfQfqGnlIqkjNbrfDbr//H0Ps5Xof5qiu1+vhH9aTGaRcQSVlAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/013a90e8b40f1b041a57862db53ba8c9/c54d4/iterm_APS.webp 175w,
/static/013a90e8b40f1b041a57862db53ba8c9/a3432/iterm_APS.webp 350w,
/static/013a90e8b40f1b041a57862db53ba8c9/426ac/iterm_APS.webp 700w,
/static/013a90e8b40f1b041a57862db53ba8c9/c139f/iterm_APS.webp 1050w,
/static/013a90e8b40f1b041a57862db53ba8c9/7f403/iterm_APS.webp 1400w,
/static/013a90e8b40f1b041a57862db53ba8c9/6e429/iterm_APS.webp 1848w&quot;
              sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/013a90e8b40f1b041a57862db53ba8c9/4edbd/iterm_APS.png 175w,
/static/013a90e8b40f1b041a57862db53ba8c9/13ae7/iterm_APS.png 350w,
/static/013a90e8b40f1b041a57862db53ba8c9/8c557/iterm_APS.png 700w,
/static/013a90e8b40f1b041a57862db53ba8c9/e996b/iterm_APS.png 1050w,
/static/013a90e8b40f1b041a57862db53ba8c9/2cefc/iterm_APS.png 1400w,
/static/013a90e8b40f1b041a57862db53ba8c9/28e7f/iterm_APS.png 1848w&quot;
            sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/013a90e8b40f1b041a57862db53ba8c9/8c557/iterm_APS.png&quot;
            alt=&quot;&amp;#39;Automatic Profile Switching&amp;#39; highlighted&quot;
            title=&quot;&amp;#39;Automatic Profile Switching&amp;#39; highlighted&quot;
            loading=&quot;lazy&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;First create a new profile, say “Production”, and under “Advanced” tab add a Trigger with the following configs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Regular Expression: &lt;code class=&quot;language-text&quot;&gt;^(\w+)@([\w.-]+):.+\$&lt;/code&gt; (amend if necessary)&lt;/li&gt;
&lt;li&gt;Action: &lt;code class=&quot;language-text&quot;&gt;Report User &amp;amp; Host&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Parameters: &lt;code class=&quot;language-text&quot;&gt;\1@\2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Instant: checked&lt;/li&gt;
&lt;li&gt;Enabled: checked&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then head back to the APS setting and insert hostnames of your production servers. Finally, for this new profile set a distinct visual feedback under the “Colors” tab; white-on-red would be fitting.&lt;/p&gt;
&lt;h3 id=&quot;badges&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#badges&quot; aria-label=&quot;badges permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Badges&lt;/h3&gt;
&lt;p&gt;Another, or additional, option is to set a badge, per iTerm pane, when a particular command gets executed. You can wrap the existing command for switching your infrastructure-related profiles and have the wrapper also execute iTerm’s &lt;code class=&quot;language-text&quot;&gt;SetBadgeFormat&lt;/code&gt; command. Here’s a sample:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;declare&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;important_profiles&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;prod&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;stag&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${important_profiles&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;*&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;}&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$profile&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token entity&quot; title=&quot;\x1B&quot;&gt;\x1B&lt;/span&gt;]1337;SetBadgeFormat=&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Careful: &lt;span class=&quot;token variable&quot;&gt;${profile}&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; base64&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token entity&quot; title=&quot;\007&quot;&gt;\007&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;regex-matching-on-longer-lines&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#regex-matching-on-longer-lines&quot; aria-label=&quot;regex matching on longer lines permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Regex matching on longer lines&lt;/h3&gt;
&lt;p&gt;In case the part of the log that you want to match on has (too) many logical lines, you should increase the following iTerm limit: &lt;strong&gt;Preferences &gt; Advanced &gt; Number of screen lines to match against trigger regular expressions&lt;/strong&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Safer shell sub-commands]]></title><description><![CDATA[How you can guarantee avoidance of deprecated commands.]]></description><link>https://merela.org/blog/safer-shell-subcommands</link><guid isPermaLink="false">https://merela.org/blog/safer-shell-subcommands</guid><pubDate>Mon, 13 Jun 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You will undoubtedly encounter deprecated, inadvisable, or outright dangerous commands in your daily work. Rather than having to rely on your erring, fallible memory, it would be better if you could &lt;em&gt;guarantee&lt;/em&gt; avoidance of such commands. Or at least have them behind an additional prompt. This is especially important in infrastructure work, where the stakes are that much higher.&lt;/p&gt;
&lt;p&gt;You might be reading the documentation on &lt;code class=&quot;language-text&quot;&gt;terraform refresh&lt;/code&gt; and come across &lt;a href=&quot;https://www.terraform.io/cli/commands/refresh#usage&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;the following warning&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Automatically applying the effect of a refresh is risky. If you have misconfigured credentials for one or more providers, Terraform may be misled into thinking that all of the managed objects have been deleted, causing it to remove all of the tracked objects without any confirmation prompt.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Rather than form a memory of the quoted bits in your hippocampus, you’d be better off storing these bits into your appropriate &lt;code class=&quot;language-text&quot;&gt;~/.{bash,zsh,etc}rc&lt;/code&gt; runcommand file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function-name function&quot;&gt;terraform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
    refresh&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      terraform apply -refresh-only
      &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    *&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token builtin class-name&quot;&gt;command&lt;/span&gt; terraform &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$@&lt;/span&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;esac&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You now have a &lt;em&gt;guarantee&lt;/em&gt; that the alternative, recommended subcommand &lt;code class=&quot;language-text&quot;&gt;apply -refresh-only&lt;/code&gt; will be used. This pattern can be applied on other dangerous and/or deprecated subcommands, be it for &lt;code class=&quot;language-text&quot;&gt;terraform&lt;/code&gt; or some other command. How &lt;code class=&quot;language-text&quot;&gt;~/.gitconfig&lt;/code&gt; allows for customized subcommands might also serve you as a further inspiration.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Packer 101]]></title><description><![CDATA[Going from 0 to having a custom-built Ubuntu AWS AMI.]]></description><link>https://merela.org/blog/packer-101</link><guid isPermaLink="false">https://merela.org/blog/packer-101</guid><pubDate>Mon, 06 Jun 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I was in the midst of modernizing client’s existing AWS infrastructure, when the client requested that one of their core server-cluster be upgraded.&lt;/p&gt;
&lt;p&gt;Since a full migration onto a container orchestrator was months in the future, I still deemed it wortwhile to make at least &lt;em&gt;some&lt;/em&gt; improvements, in the form of custom AWS AMIs. As the development team was relatively small, my goal was to lessen their need to fiddle with the servers, be it with SSHing onto it or needing to manually set up the needed monitoring tools. I also provided a fully automated deployment so that running an Ansible playbook wasn’t necessary anymore, at least for these servers.&lt;/p&gt;
&lt;h2 id=&quot;show-me-the-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#show-me-the-code&quot; aria-label=&quot;show me the code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Show me the code!&lt;/h2&gt;
&lt;p&gt;For the actual implementation, the prebaked VM needed&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a CodeDeploy agent, for automated deployments, and&lt;/li&gt;
&lt;li&gt;a Prometheus &lt;code class=&quot;language-text&quot;&gt;node-exporter&lt;/code&gt;, given the planned EKS migration.
We used Packer as our tool of choice. (If you’re fully committed to AWS you can also use ‘EC2 Image Builder’.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Presuming you have &lt;code class=&quot;language-text&quot;&gt;asdf&lt;/code&gt; installed, the zeroth step is to get Packer:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;asdf plugin-add packer https://github.com/asdf-community/asdf-hashicorp.git
asdf &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; packer &lt;span class=&quot;token number&quot;&gt;1.8&lt;/span&gt;.0
asdf &lt;span class=&quot;token builtin class-name&quot;&gt;local&lt;/span&gt; packer &lt;span class=&quot;token number&quot;&gt;1.8&lt;/span&gt;.0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the same directory, create an empty file with the &lt;code class=&quot;language-text&quot;&gt;.pkr.hcl&lt;/code&gt; extension. We’ll add the required &lt;code class=&quot;language-text&quot;&gt;packer&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;source&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;build&lt;/code&gt; blocks to this file in the following paragraphs.&lt;/p&gt;
&lt;p&gt;In &lt;code class=&quot;language-text&quot;&gt;packer&lt;/code&gt; block, pin to the same version you just installed.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;packer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;required_version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;~&gt; 1.8.0&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For easier maintenance, extract the important configs into &lt;code class=&quot;language-text&quot;&gt;locals&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;locals&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;aws_region&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;eu-west-1&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;instance_type&lt;/span&gt;          &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;t3a.micro&quot;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# If more than a single such AMI needs to be built each day, make the AMI&apos;s time-signature more granular.&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;today_date&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; formatdate(&lt;span class=&quot;token string&quot;&gt;&quot;YYYY-MM-DD&quot;&lt;/span&gt;, timestamp())
  &lt;span class=&quot;token property&quot;&gt;ami_owner_id_Canonical&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;099720109477&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;ami_ubuntu_version&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;focal-20.04&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;ami_ubuntu_arch&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;amd64&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;ami_name&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ubuntu-&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;ami_ubuntu_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;-&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;ami_ubuntu_arch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;-&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;today_date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;source&lt;/code&gt; block is pretty much standard. Setting a few well chosen &lt;code class=&quot;language-text&quot;&gt;tags&lt;/code&gt; will come helpful later, such as when provisioning servers of mixed architectures.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;source &lt;span class=&quot;token string&quot;&gt;&quot;amazon-ebs&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;custom&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;ssh_username&lt;/span&gt;          &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ubuntu&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;region&lt;/span&gt;                &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.aws_region
  &lt;span class=&quot;token property&quot;&gt;instance_type&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.instance_type
  &lt;span class=&quot;token property&quot;&gt;ami_name&lt;/span&gt;              &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.ami_name
  &lt;span class=&quot;token property&quot;&gt;force_delete_snapshot&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;launch_block_device_mappings&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;delete_on_termination&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;device_name&lt;/span&gt;           &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/dev/sda1&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;encrypted&lt;/span&gt;             &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;iops&lt;/span&gt;                  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;throughput&lt;/span&gt;            &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;125&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;volume_size&lt;/span&gt;           &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;volume_type&lt;/span&gt;           &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;gp3&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;source_ami_filter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;filters&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;                &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ubuntu/images/*ubuntu-&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;ami_ubuntu_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;-&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;ami_ubuntu_arch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;-server-*&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;root-device-type&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ebs&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;virtualization-type&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hvm&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;most_recent&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;owners&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;local.ami_owner_id_Canonical&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Name&lt;/span&gt;          &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.ami_name
    &lt;span class=&quot;token property&quot;&gt;ManagedBy&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Packer &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;packer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;version&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Base_AMI_ID&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;{{ .SourceAMI }}&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Base_AMI_Name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;{{ .SourceAMIName }}&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally the &lt;code class=&quot;language-text&quot;&gt;build&lt;/code&gt; block. This one contains two &lt;code class=&quot;language-text&quot;&gt;shell&lt;/code&gt; provisioners, and an Ansible playbook that installs an AWS CodeDeploy agent. Note that a &lt;code class=&quot;language-text&quot;&gt;provisioner&lt;/code&gt; is named from the POV of the provisioned server: a &lt;code class=&quot;language-text&quot;&gt;-local&lt;/code&gt; provisioner, such as &lt;code class=&quot;language-text&quot;&gt;ansible-local&lt;/code&gt;, runs on the server itself.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;build&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;sources&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;source.amazon-ebs.custom&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;provisioner&lt;span class=&quot;token type variable&quot;&gt; &quot;shell&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;execute_command&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;echo &apos;packer&apos; | sudo -S env {{ .Vars }} {{ .Path }}&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;script&lt;/span&gt;          &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/scripts/base.sh&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;max_retries&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;timeout&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;5m&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;provisioner&lt;span class=&quot;token type variable&quot;&gt; &quot;shell&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;execute_command&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;echo &apos;packer&apos; | sudo -S env {{ .Vars }} {{ .Path }}&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;script&lt;/span&gt;          &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/scripts/prometheus_node_exporter.sh&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;max_retries&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;timeout&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;5m&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;provisioner&lt;span class=&quot;token type variable&quot;&gt; &quot;ansible&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;playbook_file&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/scripts/codedeploy.yml&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;extra_arguments&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;--extra-vars&quot;&lt;/span&gt;,
      &lt;span class=&quot;token string&quot;&gt;&quot;aws_region=&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;aws_region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;supporting-scripts&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#supporting-scripts&quot; aria-label=&quot;supporting scripts permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Supporting scripts&lt;/h2&gt;
&lt;p&gt;Per above &lt;code class=&quot;language-text&quot;&gt;.hcl&lt;/code&gt;, I’ve placed Bash and Ansible into a sibling &lt;code class=&quot;language-text&quot;&gt;scripts&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;base.sh&lt;/code&gt; provides unattended APT upgrades:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-Eeu&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;DEBIAN_FRONTEND&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;noninteractive&quot;&lt;/span&gt;

&lt;span class=&quot;token assign-left variable&quot;&gt;apt_conf_periodic&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/etc/apt/apt.conf.d/10periodic&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;apt_conf_unattended_upgrades&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/etc/apt/apt.conf.d/50unattended-upgrades&quot;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;END&lt;span class=&quot;token bash punctuation&quot;&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$apt_conf_periodic&lt;/span&gt;&quot;&lt;/span&gt;&lt;/span&gt;
APT::Periodic::Update-Package-Lists &quot;1&quot;;
APT::Periodic::Download-Upgradeable-Packages &quot;0&quot;;
APT::Periodic::AutocleanInterval &quot;7&quot;;
APT::Periodic::Unattended-Upgrade &quot;1&quot;;
END&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;END&lt;span class=&quot;token bash punctuation&quot;&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$apt_conf_unattended_upgrades&lt;/span&gt;&quot;&lt;/span&gt;&lt;/span&gt;
Unattended-Upgrade::Allowed-Origins {
    &quot;&lt;span class=&quot;token variable&quot;&gt;${distro_id}&lt;/span&gt;:&lt;span class=&quot;token variable&quot;&gt;${distro_codename}&lt;/span&gt;&quot;;
    &quot;&lt;span class=&quot;token variable&quot;&gt;${distro_id}&lt;/span&gt;:&lt;span class=&quot;token variable&quot;&gt;${distro_codename}&lt;/span&gt;-security&quot;;
    &quot;&lt;span class=&quot;token variable&quot;&gt;${distro_id}&lt;/span&gt;ESM:&lt;span class=&quot;token variable&quot;&gt;${distro_codename}&lt;/span&gt;&quot;;
};
Unattended-Upgrade::Package-Blacklist {
};
Unattended-Upgrade::DevRelease &quot;false&quot;;
Unattended-Upgrade::Remove-Unused-Dependencies &quot;true&quot;;
END&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;chown&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-R&lt;/span&gt; root:root &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$apt_conf_periodic&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;chown&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-R&lt;/span&gt; root:root &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$apt_conf_unattended_upgrades&lt;/span&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;service&lt;/span&gt; unattended-upgrades restart&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;prometheus_node_exporter.sh&lt;/code&gt; sets up and starts Prometheus’ node/host exporter:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-Eeuo&lt;/span&gt; pipefail
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;DEBIAN_FRONTEND&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;noninteractive&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;###################################&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Config&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;node_exporter_arch&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;linux-amd64&apos;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;node_exporter_version&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;1.3.1&apos;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;###################################&lt;/span&gt;

&lt;span class=&quot;token assign-left variable&quot;&gt;new_user&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;prometheus
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useradd&lt;/span&gt; --no-create-home &lt;span class=&quot;token parameter variable&quot;&gt;--shell&lt;/span&gt; /bin/false &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$new_user&lt;/span&gt;&quot;&lt;/span&gt;


&lt;span class=&quot;token assign-left variable&quot;&gt;tar_file&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;node_exporter-&lt;span class=&quot;token variable&quot;&gt;${node_exporter_version}&lt;/span&gt;.&lt;span class=&quot;token variable&quot;&gt;${node_exporter_arch}&lt;/span&gt;.tar.gz&quot;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;wget&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://github.com/prometheus/node_exporter/releases/download/v&lt;span class=&quot;token variable&quot;&gt;${node_exporter_version}&lt;/span&gt;/&lt;span class=&quot;token variable&quot;&gt;${tar_file}&lt;/span&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;tar&lt;/span&gt; xvfz &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${tar_file}&lt;/span&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;token assign-left variable&quot;&gt;base_dir&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${tar_file&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;.tar.gz}&lt;/span&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;token assign-left variable&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/etc/prometheus&quot;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$dir&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;mv&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$base_dir&lt;/span&gt;/node_exporter&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$dir&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$tar_file&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;chown&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-R&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$new_user&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$new_user&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$dir&lt;/span&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;END&lt;span class=&quot;token bash punctuation&quot;&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; /etc/systemd/system/node_exporter.service&lt;/span&gt;
[Unit]
Description=Node Exporter
Wants=network-online.target
After=network-online.target
[Service]
User=prometheus
ExecStart=/etc/prometheus/node_exporter
[Install]
WantedBy=default.target
END&lt;/span&gt;

systemctl daemon-reload
systemctl start node_exporter
systemctl &lt;span class=&quot;token builtin class-name&quot;&gt;enable&lt;/span&gt; node_exporter&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And &lt;code class=&quot;language-text&quot;&gt;codedeploy.yml&lt;/code&gt; is an Ansible playbook that install a CodeDeploy agent:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yml&quot;&gt;&lt;pre class=&quot;language-yml&quot;&gt;&lt;code class=&quot;language-yml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;hosts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; all
  &lt;span class=&quot;token key atrule&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu
  &lt;span class=&quot;token key atrule&quot;&gt;become&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; yes
  &lt;span class=&quot;token key atrule&quot;&gt;become_user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; root
  &lt;span class=&quot;token key atrule&quot;&gt;become_method&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; sudo
  &lt;span class=&quot;token key atrule&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Install Dependencies
      &lt;span class=&quot;token key atrule&quot;&gt;apt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;pkg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;ruby-full&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;wget&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; present
        &lt;span class=&quot;token key atrule&quot;&gt;update_cache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Gather package facts
      &lt;span class=&quot;token key atrule&quot;&gt;package_facts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;manager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Install Code Deploy Agent
      &lt;span class=&quot;token key atrule&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Fetch the CodeDeploy install script
          &lt;span class=&quot;token key atrule&quot;&gt;get_url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://aws-codedeploy-{{ aws_region }}.s3.{{ aws_region }}.amazonaws.com/latest/install&quot;&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /tmp/codedeploy&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;install
            &lt;span class=&quot;token key atrule&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0700&lt;/span&gt;

        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Run the installation script
          &lt;span class=&quot;token key atrule&quot;&gt;become&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /tmp/codedeploy&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;install auto
      &lt;span class=&quot;token key atrule&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&apos;codedeploy-agent&apos; not in ansible_facts.packages&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once you have all the above in place, building your AMI is merely a matter of:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;a.hcl&apos;&lt;/span&gt;
packer &lt;span class=&quot;token function&quot;&gt;fmt&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$input&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; packer validate &lt;span class=&quot;token variable&quot;&gt;$input&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; packer build &lt;span class=&quot;token variable&quot;&gt;$input&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can manually verify that the desired services were indeed provisioned. Instantiate an EC2 off of the newly minted AMI and run:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;service&lt;/span&gt; unattended-upgrades status
&lt;span class=&quot;token function&quot;&gt;service&lt;/span&gt; codedeploy-agent status&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;closing-tips&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#closing-tips&quot; aria-label=&quot;closing tips permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Closing tips&lt;/h2&gt;
&lt;p&gt;The most general tip is to enable debug logs when you encounter an issue, using &lt;code class=&quot;language-text&quot;&gt;PACKER_LOG=1&lt;/code&gt; before a Packer command. More specific tips are below, each in its own section.&lt;/p&gt;
&lt;h3 id=&quot;builds-finished-but-no-artifacts-were-created&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#builds-finished-but-no-artifacts-were-created&quot; aria-label=&quot;builds finished but no artifacts were created permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;“Builds finished but no artifacts were created.”&lt;/h3&gt;
&lt;p&gt;If you encounter this error, recheck whether a &lt;code class=&quot;language-text&quot;&gt;source&lt;/code&gt; actually &lt;em&gt;is&lt;/em&gt; being referred to in your &lt;code class=&quot;language-text&quot;&gt;build&lt;/code&gt; block:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;source &lt;span class=&quot;token string&quot;&gt;&quot;amazon-ebs&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;custom&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;  ...
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;build&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;sources&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;source.amazon-ebs.custom&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;  ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;packer-console&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#packer-console&quot; aria-label=&quot;packer console permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Packer console&lt;/h3&gt;
&lt;p&gt;If you wish to use &lt;code class=&quot;language-text&quot;&gt;packer console&lt;/code&gt; for anything more than mere variable evaluation, such as&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;timestamp&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; packer console&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;you have to start it with the &lt;code class=&quot;language-text&quot;&gt;--config-type=hcl2&lt;/code&gt; flag. Only then will you also be able to evaluate HCL expressions:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; formatdate&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;YYYY-MM-DD&quot;&lt;/span&gt;, timestamp&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2022&lt;/span&gt;-04-24&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;ansible-and-unrecognized-arguments&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ansible-and-unrecognized-arguments&quot; aria-label=&quot;ansible and unrecognized arguments permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Ansible and “unrecognized arguments”&lt;/h3&gt;
&lt;p&gt;You might encounter this in the logs, leading to Packer exiting.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;amazon-ebs.custom: ansible-playbook: error: unrecognized arguments: &lt;code class=&quot;language-text&quot;&gt;/path/to/codedeploy.yml&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If Ansible gives you trouble, verify that you’re quoting &lt;code class=&quot;language-text&quot;&gt;extra-vars&lt;/code&gt; correctly; per docs, “arguments should not be quoted”.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff-hcl&quot;&gt;&lt;code class=&quot;language-diff-hcl&quot;&gt;&lt;span class=&quot;token deleted-sign deleted language-hcl&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt; not OK
&lt;/span&gt;extra_arguments = [
&lt;span class=&quot;token unchanged language-hcl&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--extra-vars \&quot;foo=&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;\&quot;&quot;&lt;/span&gt;
&lt;/span&gt;]

&lt;span class=&quot;token inserted-sign inserted language-hcl&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; OK
&lt;/span&gt;extra_arguments = [
&lt;span class=&quot;token unchanged language-hcl&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--extra-vars&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;foo=&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
&lt;/span&gt;]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;next-up&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#next-up&quot; aria-label=&quot;next up permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Next up&lt;/h2&gt;
&lt;p&gt;In other articles, we’ll see how we can make use of the now-baked-in &lt;code class=&quot;language-text&quot;&gt;node-exporter&lt;/code&gt; for monitoring with Prometheus. And how we can leverage the AWS CodeDeploy agent for automated deployments onto EC2 instances.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Scylla migration]]></title><description><![CDATA[How a no-downtime migration was made and RBAC policies were enacted.]]></description><link>https://merela.org/blog/scylladb-migration-and-rbac-setup</link><guid isPermaLink="false">https://merela.org/blog/scylladb-migration-and-rbac-setup</guid><pubDate>Tue, 01 Feb 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Scylla is a Apache Cassandra-compatible NoSQL data store, with a significantly improved performance. I have recently successfully migrated a self-managed Scylla cluster to a managed, Cloud Scylla. Here are a few takeaways.&lt;/p&gt;
&lt;p&gt;First, from a business perspective, the migration made sense as the team was too small to justify spending further engineering time in order to (safely) keep up with the required upgrades. The business had predictable usage patterns, but couldn’t afford any downtime; and the data was large enough that the estimated downtime would’ve been measured in &lt;em&gt;hours&lt;/em&gt;. So we chose to migrate using a transitional period of dual-writes, a fork &amp;#x26; lift operation for the older data with &lt;a href=&quot;https://github.com/scylladb/scylla-migrator&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;scylla-migrator&lt;/a&gt;, and finally kept the old cluster for a verification period of dual-reads.&lt;/p&gt;
&lt;p&gt;Meanwhile I also updated the services using Scylla and have them autodiscover the nodes, courtesy of a particular client-side library. On the cluster side, I also enacted RBAC for both 1) backend services and 2) QA &amp;#x26; developer team. Whereas previously they all shared an unlimited superuser role, now they each have their own role, with least privileges necessary:&lt;/p&gt;
&lt;p&gt;For backend services:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;create&lt;/span&gt; role app1 &lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;app1&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;and&lt;/span&gt; login &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;create&lt;/span&gt; role app2 &lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;app2&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;and&lt;/span&gt; login &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;-- Only grant the needed rights to each app.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;grant&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; keyspace akeyspace &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; app1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;grant&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; keyspace akeyspace &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; app2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;grant&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;modify&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; akeyspace&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;atable   &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; app2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For QA team and developers:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;-- Implies `SUPERUSER = false AND LOGIN = false`.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;create&lt;/span&gt; role qa&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;grant&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; keyspace akeyspace &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; qa&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;grant&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;modify&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; akeyspace&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;atable &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; qa&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;create&lt;/span&gt; role alice &lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;alice&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;and&lt;/span&gt; login &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;grant&lt;/span&gt; qa &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; alice&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;create&lt;/span&gt; role readonly&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;grant&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; keyspace akeyspace &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; qa&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;create&lt;/span&gt; role bob &lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;bob&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;and&lt;/span&gt; login &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;grant&lt;/span&gt; readonly &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; bob&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Thus, no more risking that a fatfingered QA team member, or a developer indirectly through one of the apps, would execute a &lt;code class=&quot;language-text&quot;&gt;DROP&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;ALTER&lt;/code&gt;, or other dangerous command. Unfortunately, &lt;code class=&quot;language-text&quot;&gt;MODIFY&lt;/code&gt; cannot be made more granular so as to allow a &lt;code class=&quot;language-text&quot;&gt;DELETE&lt;/code&gt; but &lt;em&gt;not&lt;/em&gt; a &lt;code class=&quot;language-text&quot;&gt;TRUNCATE&lt;/code&gt;. This limitation is unlikely to disappear, since Cassandra-compatibility must be kept intact.&lt;/p&gt;
&lt;p&gt;A nice touch was to also enable workload prioritization, available only with Scylla Enterprise:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; SERVICE_LEVEL olap &lt;span class=&quot;token keyword&quot;&gt;WITH&lt;/span&gt; SHARES &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; SERVICE_LEVEL oltp &lt;span class=&quot;token keyword&quot;&gt;WITH&lt;/span&gt; SHARES &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

ATTACH SERVICE_LEVEL olap &lt;span class=&quot;token keyword&quot;&gt;TO&lt;/span&gt; qa&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
ATTACH SERVICE_LEVEL olap &lt;span class=&quot;token keyword&quot;&gt;TO&lt;/span&gt; readonly&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
ATTACH SERVICE_LEVEL oltp &lt;span class=&quot;token keyword&quot;&gt;TO&lt;/span&gt; app1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
ATTACH SERVICE_LEVEL oltp &lt;span class=&quot;token keyword&quot;&gt;TO&lt;/span&gt; app2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally, to review the changes made:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;LIST &lt;span class=&quot;token keyword&quot;&gt;ALL&lt;/span&gt; PERMISSIONS &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;OF&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;role&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
LIST &lt;span class=&quot;token keyword&quot;&gt;ALL&lt;/span&gt; ATTACHED SERVICE_LEVELS&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Disable Mac App Store notifications]]></title><link>https://merela.org/blog/disable-mac-app-store-notifications</link><guid isPermaLink="false">https://merela.org/blog/disable-mac-app-store-notifications</guid><pubDate>Sun, 16 Jan 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Since App Store isn’t listed under System Preferences &gt; Notifications, here’s a workaround that indirectly disables its notifications by setting the “last” update far into the future.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;defaults &lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  com.apple.appstored LastUpdateNotification &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-date&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2099-12-12 12:00:00 +0000&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[RabbitMQ, OAuth2, and ConduitAMQP]]></title><description><![CDATA[How misreading of a library's documentation led to spelunking in RabbitMQ source code.]]></description><link>https://merela.org/blog/RabbitMQ-OAuth2-ConduitAMQP</link><guid isPermaLink="false">https://merela.org/blog/RabbitMQ-OAuth2-ConduitAMQP</guid><pubDate>Sun, 14 Nov 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://github.com/pma/amqp&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;amqp&lt;/a&gt; is one of the most used RabbitMQ Elixir libraries used by many projects directly and also a dependency of &lt;a href=&quot;https://github.com/dashbitco/broadway_rabbitmq/blob/054b2b9dbdf78f98ec9f33fc0a4a701999377953/mix.lock#L2&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;Broadway RabbitMQ&lt;/a&gt;. It does its job well, but it lacks few polishing touches that are usually needed in a ‘real world’ usage. F.e. you’re on your own when it comes to handling connection hickups. Also, its README presumes at least somewhat experienced user. One knowing that it wouldn’t be OK to merely &lt;code class=&quot;language-text&quot;&gt;spawn&lt;/code&gt; a “bare” process for the &lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token function&quot;&gt;consume&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;/code&gt;, as it was suggested in an older README version. But also a user that knows that even the proposed &lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token module class-name&quot;&gt;Task&lt;/span&gt;&lt;/code&gt; should often rather be a supervised one, using &lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token module class-name&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Supervisor&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I have already equipped &lt;code class=&quot;language-text&quot;&gt;amqp&lt;/code&gt; with connection monitoring in previous projects, using &lt;code class=&quot;language-text&quot;&gt;:gen_statem&lt;/code&gt; behaviour and &lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token module class-name&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;monitor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;conn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pid&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;. It worked, and it also allowed the whole service to boot even if the AMQP broker was unreachable at that moment. But &lt;code class=&quot;language-text&quot;&gt;:gen_statem&lt;/code&gt; implementation was bit verbose, so this time I picked &lt;a href=&quot;https://github.com/conduitframework/conduit_amqp&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;ConduitAMQP&lt;/a&gt; for a new project. The library helpfuly builds on top of AMQP and leverages &lt;a href=&quot;https://github.com/elixir-ecto/connection&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;Connection&lt;/a&gt; to provide resiliency.&lt;/p&gt;
&lt;h2 id=&quot;an-issue-encountered&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#an-issue-encountered&quot; aria-label=&quot;an issue encountered permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;An issue encountered&lt;/h2&gt;
&lt;p&gt;Though, as this project required an AMQP connection to be authenticated via OAuth2, and as often more code means more bugs, I hit upon an issue where ConduitAMQP library wasn’t relaying connection options to its amqp dependency, as summarized in the following image.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 69.71428571428572%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB90lEQVQ4y41Ty47TQBDMR/FbfAAnzlwRVy6IC4cV0q4EggVELKEwCSgSGykP5Z3Yjh+x40mcd1JMDcxovWiBlko9Ho+rq6vHpX6/jyAIwDyfz3FfnM9n/E+UVqsV9vs90jSFlBLb7RbL5dIiyzIcj0dLeh/M+1IURRiNRhpUOplMwCIk3u12GqfT6Z8KzfsS20ySRKtZr9da6d+C5HdRUEjC2WwG13UxGAw0IclvK2M2ttDr8XisO+J54rYlmpBtNptNOxgqXSwW2lMG1/SShbrdriYx54fDoS5oCXk4z3O02239EdcEbaCnvu/rTHXcZzE+t1otXYB7VG8JSWJaoToeMIq4piKSGNXM0+nUnucACy2bSXJzrzKny6CyWq2mW2s0GqjX6+h0Orq4uWIsQFDpZrP5RXhn+HYlZYZerwfP85Sanm6RJFRIVYQhYieW0EyRthowgjCEH4TIFMlUkfrqJuSKhFYYS4jD4VD8U0L1oVzlyEIPWeuryi7k4AbhzEcWqEuexlgmEeI4RKh+Atf37A2gwjiOi0PB+bemXQI8eQBcPQJ+XADtt8DNa6D5BuiXIdMAbhwhkYs/LnuB0HHKqH37DvHFgXj6EOL5Y4jrS4hXzyCuXkJcvkD13QXKn97js+Pg+uMHlFUWQqBaraJSqWhfDeFPapYnI/8RhKUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/d798da3f7430c213d4641a9a612c6c5f/c54d4/ConduitAMQP_to_RabbitMQ.webp 175w,
/static/d798da3f7430c213d4641a9a612c6c5f/a3432/ConduitAMQP_to_RabbitMQ.webp 350w,
/static/d798da3f7430c213d4641a9a612c6c5f/426ac/ConduitAMQP_to_RabbitMQ.webp 700w,
/static/d798da3f7430c213d4641a9a612c6c5f/c139f/ConduitAMQP_to_RabbitMQ.webp 1050w,
/static/d798da3f7430c213d4641a9a612c6c5f/7f403/ConduitAMQP_to_RabbitMQ.webp 1400w,
/static/d798da3f7430c213d4641a9a612c6c5f/5540a/ConduitAMQP_to_RabbitMQ.webp 1844w&quot;
              sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/d798da3f7430c213d4641a9a612c6c5f/4edbd/ConduitAMQP_to_RabbitMQ.png 175w,
/static/d798da3f7430c213d4641a9a612c6c5f/13ae7/ConduitAMQP_to_RabbitMQ.png 350w,
/static/d798da3f7430c213d4641a9a612c6c5f/8c557/ConduitAMQP_to_RabbitMQ.png 700w,
/static/d798da3f7430c213d4641a9a612c6c5f/e996b/ConduitAMQP_to_RabbitMQ.png 1050w,
/static/d798da3f7430c213d4641a9a612c6c5f/2cefc/ConduitAMQP_to_RabbitMQ.png 1400w,
/static/d798da3f7430c213d4641a9a612c6c5f/80cfc/ConduitAMQP_to_RabbitMQ.png 1844w&quot;
            sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/d798da3f7430c213d4641a9a612c6c5f/8c557/ConduitAMQP_to_RabbitMQ.png&quot;
            alt=&quot;The issue summarized&quot;
            title=&quot;The issue summarized&quot;
            loading=&quot;lazy&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So I got blocked for quite some time, by not being able to authenticate against RabbitMQ, even though the Keycloak-vended JWT had all the expected fields set. Luckily, succesfully using the same token against RabbitMQ’s management/HTTP API was a hint that the issue might lie with the ConduitAMQP library itself.&lt;/p&gt;
&lt;p&gt;First I checked RabbitMQ’s own logs, but they were a bit sparse&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;HTTP access denied: Authentication using an OAuth 2/JWT token failed: provided token is invalid.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;giving no details of the root cause of invalidity.&lt;/p&gt;
&lt;h2 id=&quot;going-down-the-rabbitmq-hole&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#going-down-the-rabbitmq-hole&quot; aria-label=&quot;going down the rabbitmq hole permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Going down the RabbitMQ hole&lt;/h2&gt;
&lt;p&gt;So I was left with no other choice but to amend and build RabbitMQ from its source code. First I exposed &lt;code class=&quot;language-text&quot;&gt;Err&lt;/code&gt; to the logs, making the following change in &lt;code class=&quot;language-text&quot;&gt;rabbit_auth_backend_oauth2.erl&lt;/code&gt;,&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight has-highlighted-lines&quot; data-language=&quot;erlang&quot;&gt;&lt;pre class=&quot;language-erlang&quot;&gt;&lt;code class=&quot;language-erlang&quot;&gt;@@ &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;85&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;85&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt; @@ &lt;span class=&quot;token function&quot;&gt;update_state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;AuthUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;NewToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;check_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;NewToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;%% avoid logging the token&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;E&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom&quot;&gt;refused&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom&quot;&gt;invalid_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;_Err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;_Stacktrace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom&quot;&gt;refused&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Authentication using an OAuth 2/JWT token failed: provided token is invalid&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom&quot;&gt;refused&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom&quot;&gt;invalid_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;_Stacktrace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom&quot;&gt;refused&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Authentication using an OAuth 2/JWT token failed: provided token is invalid: ~p&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom&quot;&gt;refused&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
       &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom&quot;&gt;refused&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom&quot;&gt;rabbit_misc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Authentication using an OAuth 2/JWT token failed: ~p&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;DecodedToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;then set the relevant config in a &lt;code class=&quot;language-text&quot;&gt;.config&lt;/code&gt; file,&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight has-highlighted-lines&quot; data-language=&quot;erlang&quot;&gt;&lt;pre class=&quot;language-erlang&quot;&gt;&lt;code class=&quot;language-erlang&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom&quot;&gt;rabbit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;% Other settings, f.e.&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;% {log_levels, [ ... ]},&lt;/span&gt;
&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and finally ran &lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt; run-broker &lt;span class=&quot;token assign-left variable&quot;&gt;RABBITMQ_CONFIG_FILE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;above.config&lt;/code&gt;. Thanks to the above config, RabbitMQ’s console now finally clued me in to the actual cause of token invalidity&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;HTTP access denied: Authentication using an OAuth 2/JWT token failed: provided token is invalid: &lt;code class=&quot;language-text&quot;&gt;{badarg, [&amp;lt;&amp;lt;&quot;guest&quot;&gt;&gt;]}&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;which made me realize that &lt;code class=&quot;language-text&quot;&gt;Conduit AMQP&lt;/code&gt; likely wasn’t respecting the provided options, &lt;code class=&quot;language-text&quot;&gt;username: &quot;&quot;&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;password: token&lt;/code&gt;, and thus making the underlying &lt;code class=&quot;language-text&quot;&gt;amqp&lt;/code&gt; library falling back to &lt;code class=&quot;language-text&quot;&gt;&quot;guest&quot;&lt;/code&gt; as the default &lt;code class=&quot;language-text&quot;&gt;username&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;password&lt;/code&gt;. Actually, the culprit was me, at least initially, as I missed the documentation’s nuanced point of exclusivity between the &lt;code class=&quot;language-text&quot;&gt;:url&lt;/code&gt; and other options; &lt;a href=&quot;https://github.com/conduitframework/conduit_amqp/pull/88&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;I’ve since emphasized this exclusivity&lt;/a&gt; for future users. But even better, I’ve later also realized that amqp library supports both URI &lt;em&gt;and&lt;/em&gt; options since 1.3.0, I’ve updated ConduitAMQP to make use of this.&lt;/p&gt;
&lt;p&gt;(Should you also want to run RabbitMQ from its source, I’ve had success with these &lt;a href=&quot;https://asdf-vm.com/&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;asdf&lt;/a&gt;-installed tools:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;make   4.3
bazel  0.13.0
erlang 24.0.5
elixir 1.12.2-otp-24
rebar  3.16.1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And I also needed to &lt;code class=&quot;language-shell&quot;&gt;brew &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; libxsl xmlto&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can also do without a custom &lt;code class=&quot;language-text&quot;&gt;.config&lt;/code&gt; file and just &lt;code class=&quot;language-text&quot;&gt;tail&lt;/code&gt; a logfile, whose full path is printed just as RabbitMQ finishes booting up.)&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;One lesson of this investigation was that it’s often most effective to just dive into the source code itself. I was also reassured by how relatively approachable foreign codebases can be, when written in a functional language, Erlang &amp;#x26; Elixir in this case.&lt;/p&gt;
&lt;p&gt;While spelunking around RabbitMQ’s code I also realised how somewhat arbitrary &lt;a href=&quot;https://github.com/rabbitmq/rabbitmq-server/blob/059b44ff0cabc880cacaa932f9fc4199820b56b2/deps/rabbitmq_auth_backend_oauth2/src/rabbit_auth_backend_oauth2.erl#L207-L208&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;the current implementation of Keycloak support&lt;/a&gt; is. I don’t think that the token format presented in &lt;a href=&quot;https://github.com/rabbitmq/rabbitmq-auth-backend-oauth2/issues/36&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;issue #36&lt;/a&gt;, which then prompted &lt;a href=&quot;https://github.com/rabbitmq/rabbitmq-auth-backend-oauth2/pull/38/files&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;the PR for Keycloak support&lt;/a&gt;, is in any way specific to Keycloak. Rather, the format shown in the issue, with &lt;code class=&quot;language-text&quot;&gt;&quot;permissions&quot;&lt;/code&gt; nested under an &lt;code class=&quot;language-text&quot;&gt;&quot;authorization&quot;&lt;/code&gt; field, is just &lt;em&gt;a&lt;/em&gt; possible format that the issue-author just happened to be using, and can be easily replicated as shown in below image.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 22.285714285714285%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAj0lEQVQY051Pyw7DIAzj/z+1hfZAgYQArsNp0nZapMiO8zAE6QbtHf/EWusrg4rgyRkyFkqfMDO0JhhjEBtUdWvGutPcuWve/xVB2KyloKohi0Hc4Hn2kmOpdWs5k3PO9W3EX02+aM6J4UkD58GXzvPkMI9I28N+4BNVBdd14TgO3Pe9eUoJkXspRkTmrokvglw44mMioU4AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/20ff035f3bf599677012e9e840d9f783/c54d4/keycloak_rabbitmq_adhoc_format.webp 175w,
/static/20ff035f3bf599677012e9e840d9f783/a3432/keycloak_rabbitmq_adhoc_format.webp 350w,
/static/20ff035f3bf599677012e9e840d9f783/426ac/keycloak_rabbitmq_adhoc_format.webp 700w,
/static/20ff035f3bf599677012e9e840d9f783/c139f/keycloak_rabbitmq_adhoc_format.webp 1050w,
/static/20ff035f3bf599677012e9e840d9f783/7f403/keycloak_rabbitmq_adhoc_format.webp 1400w,
/static/20ff035f3bf599677012e9e840d9f783/a02b9/keycloak_rabbitmq_adhoc_format.webp 1852w&quot;
              sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/20ff035f3bf599677012e9e840d9f783/4edbd/keycloak_rabbitmq_adhoc_format.png 175w,
/static/20ff035f3bf599677012e9e840d9f783/13ae7/keycloak_rabbitmq_adhoc_format.png 350w,
/static/20ff035f3bf599677012e9e840d9f783/8c557/keycloak_rabbitmq_adhoc_format.png 700w,
/static/20ff035f3bf599677012e9e840d9f783/e996b/keycloak_rabbitmq_adhoc_format.png 1050w,
/static/20ff035f3bf599677012e9e840d9f783/2cefc/keycloak_rabbitmq_adhoc_format.png 1400w,
/static/20ff035f3bf599677012e9e840d9f783/ec5f6/keycloak_rabbitmq_adhoc_format.png 1852w&quot;
            sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/20ff035f3bf599677012e9e840d9f783/8c557/keycloak_rabbitmq_adhoc_format.png&quot;
            alt=&quot;Reproducing nested &amp;quot;permissions&amp;quot; in Keycloak&quot;
            title=&quot;Reproducing nested &amp;quot;permissions&amp;quot; in Keycloak&quot;
            loading=&quot;lazy&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In short, I don’t think it’s warranted that RabbitMQ expects this exact structure to be present. Perhaps the existing &lt;code class=&quot;language-text&quot;&gt;&quot;scope&quot;&lt;/code&gt; field, also used by &lt;a href=&quot;https://github.com/cloudfoundry/uaa&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;UAA&lt;/a&gt;, could be extended to also allow such nested structures.&lt;/p&gt;
&lt;p&gt;And another lesson is, as always, to RTM.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Jupyter tips]]></title><link>https://merela.org/blog/jupyter-tips</link><guid isPermaLink="false">https://merela.org/blog/jupyter-tips</guid><pubDate>Tue, 25 May 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;As I do data-related projects only on a semi-regular basis, I often find myself regrouping from the context switch. There’s not much one can do about the inherent complexity of the subject, but maybe we can lessen the incidental one.&lt;/p&gt;
&lt;p&gt;One of the things I often do is amend my Jupyter workflow with slight ergonomic improvements. Gathering the following tips will help my future self make this context switch a bit easier; maybe you’ll find them helpful as well.&lt;/p&gt;
&lt;h2 id=&quot;stable-test-set-split&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stable-test-set-split&quot; aria-label=&quot;stable test set split permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Stable test-set split&lt;/h2&gt;
&lt;p&gt;One thing I often find myself in need of is a future-stable ID for a test-set split and I haven’t found an option in &lt;code class=&quot;language-text&quot;&gt;pandas.train_test_split&lt;/code&gt; that would offer this. &lt;a href=&quot;/contact/&quot;&gt;Let me know&lt;/a&gt; if I missed it, or if there’s an equivalent in some other package. In lieu of that I often use the following &lt;code class=&quot;language-text&quot;&gt;split_train_test_by_id&lt;/code&gt;, which splits the data based on each record’s last-hash-byte membership in the &lt;code class=&quot;language-text&quot;&gt;[0, test_ratio*256)&lt;/code&gt; range.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight has-highlighted-lines&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; hashlib &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; md5

&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;_is_in_test_set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id_&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; test_ratio&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  id_hash &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; md5&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;encode&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;utf-8&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;digest&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; id_hash&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; test_ratio &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;256&lt;/span&gt;

&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;split_train_test_by_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; test_ratio&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id_column&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;/span&gt;  in_test_set &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;id_column&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;lambda&lt;/span&gt; id_&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; _is_in_test_set&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id_&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; test_ratio&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;loc&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;in_test_set&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;loc&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;in_test_set&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

df_train&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; df_test &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; split_train_test_by_id&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; test_ratio&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id_column&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;an_id_column&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;shortcuts&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#shortcuts&quot; aria-label=&quot;shortcuts permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Shortcuts&lt;/h2&gt;
&lt;p&gt;One can rebind some built-in commands either A) through the web interface or B) via the &lt;code class=&quot;language-text&quot;&gt;&quot;keys&quot;&lt;/code&gt; object in &lt;code class=&quot;language-text&quot;&gt;~/.jupyter/nbconfig/notebook.json&lt;/code&gt;. Any new, custom shortcuts should go to &lt;code class=&quot;language-text&quot;&gt;~/.jupyter/custom/&lt;/code&gt;. Or, each such &lt;code class=&quot;language-text&quot;&gt;%%javascript&lt;/code&gt; cell should be execute per notebook.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;javascript
Jupyter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keyboard_manager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;edit_shortcuts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add_shortcut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;ctrl-alt-k&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;help&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Kill line&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;help_index&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;zz&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token function-variable function&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; cm &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;notebook&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_selected_cell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;code_mirror
    cm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;execCommand&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;goLineStart&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    cm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;execCommand&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;killLine&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    cm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;execCommand&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;delCharAfter&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As mentioned, rather than keep copy pasting above custom shortcuts between notebooks, a better solution would be to have them automatically set on startup.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js:title=$home/.jupyter/custom/custom.js&quot;&gt;&lt;pre class=&quot;language-js:title=$home/.jupyter/custom/custom.js&quot;&gt;&lt;code class=&quot;language-js:title=$home/.jupyter/custom/custom.js&quot;&gt;$([IPython.events]).on(&amp;#39;app_initialized.NotebookApp&amp;#39;, function(){
  // WIP: have the command duplicate a line robustly.
  CodeMirror.keyMap.macDefault[&amp;quot;cmd-shift-d&amp;quot;] = function(cm){
    var current_cursor = cm.doc.getCursor();
    var line_content = cm.doc.getLine(current_cursor.line);
    CodeMirror.commands.goLineEnd(cm);
    CodeMirror.commands.newlineAndIndent(cm);
    cm.doc.replaceSelection(line_content);
    cm.doc.setCursor(current_cursor.line + 1, current_cursor.ch);
  };
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;better-plotting-defaults&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#better-plotting-defaults&quot; aria-label=&quot;better plotting defaults permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Better plotting defaults&lt;/h2&gt;
&lt;p&gt;Arguably better plotting defaults, especially the usage of &lt;code class=&quot;language-text&quot;&gt;ConciseDateConverter&lt;/code&gt; that keeps the axes from being overcrowded and unreadable.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight has-highlighted-lines&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; matplotlib&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pyplot &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; plt
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; seaborn &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; sns
&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;matplotlib inline
plt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rcParams&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;figure.figsize&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
sns&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; datetime
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; matplotlib&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dates &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; mdates
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; matplotlib&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;units &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; munits
&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;converter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mdates&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ConciseDateConverter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;munits&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;registry&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;np&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;datetime64&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; converter
munits&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;registry&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;date&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; converter
munits&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;registry&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; converter&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Bash defer idiom]]></title><link>https://merela.org/blog/bash_defer_idiom_with_trap</link><guid isPermaLink="false">https://merela.org/blog/bash_defer_idiom_with_trap</guid><pubDate>Thu, 23 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;One use case of &lt;code class=&quot;language-text&quot;&gt;trap&lt;/code&gt; in Bash is the following snippet:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight has-highlighted-lines&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;tempfile&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;/tmp/tmpdata
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-name function&quot;&gt;cleanup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$tempfile&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;trap&lt;/span&gt; cleanup EXIT&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The highlighted line in particular might be considered an equivalent of a &lt;code class=&quot;language-text&quot;&gt;defer&lt;/code&gt; statement in, say, Swift or Go.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[awk's seen idiom]]></title><description><![CDATA[Distinctifying a CSV column using awk alone.]]></description><link>https://merela.org/blog/AwkIdiom</link><guid isPermaLink="false">https://merela.org/blog/AwkIdiom</guid><pubDate>Mon, 13 May 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I wanted to extract distinct values of a particular column in a csv file, and just found &lt;code class=&quot;language-text&quot;&gt;awk&lt;/code&gt; has a concise idiom to do just that. Say you want to operate on the 2nd column, hence &lt;code class=&quot;language-text&quot;&gt;$2&lt;/code&gt; in the following snippet. This is all it takes:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;awk&lt;/span&gt; -F&lt;span class=&quot;token string&quot;&gt;&apos;,&apos;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;!seen[$2]++&apos;&lt;/span&gt; a.csv&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;seen&lt;/code&gt; is merely an example. You may use (at least) anything from the &lt;code class=&quot;language-text&quot;&gt;[A-Za-z_]+&lt;/code&gt; domain.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Automated permissions testing]]></title><description><![CDATA[Permissions testing via an extended XCUIApplication, ready to interact with iOS SpringBoard.]]></description><link>https://merela.org/blog/automated-permissions-testing</link><guid isPermaLink="false">https://merela.org/blog/automated-permissions-testing</guid><pubDate>Wed, 06 Sep 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Previously we’ve covered &lt;a href=&quot;automated-deeplinks-testing.html&quot;&gt;deeplinks with automated tests&lt;/a&gt;. Let’s now do the same for permissions. We’ll only showcase doing so for notifications, but the same would apply to others: camera, photos, location, calendar, reminders, microphone, etc.&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#prerequisites&quot; aria-label=&quot;prerequisites permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;First we need to empower UI testing with ability to interact with SpringBoard; and thus be able to delete the app-under-test between test cases.&lt;/p&gt;
&lt;p&gt;Download private headers &lt;code class=&quot;language-text&quot;&gt;XCUIApplication.h&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;XCUIElement.h&lt;/code&gt; from &lt;a href=&quot;https://github.com/facebook/WebDriverAgent/tree/master/PrivateHeaders/XCTest&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;WebDriverAgent&lt;/a&gt;. Then add a bridge header file, we’ll name it &lt;code class=&quot;language-text&quot;&gt;Bridge.h&lt;/code&gt;, that imports the downloaded headers:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;c&quot;&gt;&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Bridge.h&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;XCUIApplication.h&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;XCUIElement.h&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For the test-target, set &lt;code class=&quot;language-text&quot;&gt;Objective-C Bridging Header&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;${ROOT}/&amp;lt;MyAppTests&gt;/.../Bridge.h&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.57142857142857%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAABYlAAAWJQFJUiTwAAABUklEQVQoz12RzU7DMBCE8/7v0CtFwAtwAYkDFaWiAg6A1CKqorZpEie2Ezv+y+A15FBWWm1kj2c+bbJj3cE6j0oo9FpBKQVrLbTWaNsWwVl4a4DBo7cO2nq4eC+lhDEmNem89wghIMtZi2PFMASPYQA458m0ibOuCiw2BsutweWzx/WHg4qhXaeSjozKsgRjLEE455Ap3WNXciS3WJQkhEhiIt5XEocYutoJbHMOa3TSjFoyITKayZBVJbZfaxzyPKV2XYemaVJyURTgTY2aVeiVhFZtIqGiwHE9wx8MVUY0owHNNhoKIaM5nfP0rXsD5wPCQFSRJpL1dBaJfKQj0rEz/Cvape4kJK/RigamV+PNSQcfdxZ/Vgj+5H1GroQcwi/2205jOstxfr/H1UOOi9kBk9tvTO5yTB8FzuZNbIGblx3m73ssViWWnzWe1gyvG44fu8wYj1Nn2ssAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/fa7757cb7de1d1b90ff9eaf79c3059f6/c54d4/bridgingheader.webp 175w,
/static/fa7757cb7de1d1b90ff9eaf79c3059f6/a3432/bridgingheader.webp 350w,
/static/fa7757cb7de1d1b90ff9eaf79c3059f6/426ac/bridgingheader.webp 700w,
/static/fa7757cb7de1d1b90ff9eaf79c3059f6/c139f/bridgingheader.webp 1050w,
/static/fa7757cb7de1d1b90ff9eaf79c3059f6/7f403/bridgingheader.webp 1400w,
/static/fa7757cb7de1d1b90ff9eaf79c3059f6/ae1af/bridgingheader.webp 1512w&quot;
              sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/fa7757cb7de1d1b90ff9eaf79c3059f6/4edbd/bridgingheader.png 175w,
/static/fa7757cb7de1d1b90ff9eaf79c3059f6/13ae7/bridgingheader.png 350w,
/static/fa7757cb7de1d1b90ff9eaf79c3059f6/8c557/bridgingheader.png 700w,
/static/fa7757cb7de1d1b90ff9eaf79c3059f6/e996b/bridgingheader.png 1050w,
/static/fa7757cb7de1d1b90ff9eaf79c3059f6/2cefc/bridgingheader.png 1400w,
/static/fa7757cb7de1d1b90ff9eaf79c3059f6/985a9/bridgingheader.png 1512w&quot;
            sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/fa7757cb7de1d1b90ff9eaf79c3059f6/8c557/bridgingheader.png&quot;
            alt=&quot;Bridging Header setting&quot;
            title=&quot;Bridging Header setting&quot;
            loading=&quot;lazy&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now we start using the newly gotten private APIs:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;XCTest&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Springboard&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; springboard &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;XCUIApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;privateWithPath&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bundleID&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;com.apple.springboard&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Add a helper that deletes our app-under-test:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;deleteMyApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;XCUIApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;terminate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Resolve the query for the SpringBoard rather than launching it&lt;/span&gt;
  springboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; icon &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; springboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;icons&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;MyApp&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;XCTAssert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;icon&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isHittable&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Bring up the little &quot;X&quot; button to delete the app&lt;/span&gt;
  icon&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;press&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;forDuration&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// The little &quot;X&quot; button is, it seems, not exposed directly&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; xButtonCoordinate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGVector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dx&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;icon&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;frame&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;minX &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; springboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;frame&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maxX&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                   dy&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;icon&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;frame&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;minY &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; springboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;frame&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maxY&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Delete the app&lt;/span&gt;
  springboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;coordinate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;withNormalizedOffset&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; xButtonCoordinate&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  springboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;alerts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;buttons&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Delete&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Stop icons from jiggling&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;XCUIDevice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shared&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;press&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;home&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(Sidenote: why &lt;code class=&quot;language-text&quot;&gt;class&lt;/code&gt; rather than &lt;code class=&quot;language-text&quot;&gt;static&lt;/code&gt;? First one is 1 character shorter. Joking aside, it’s a toss-up since &lt;code class=&quot;language-text&quot;&gt;Springboard&lt;/code&gt; class is &lt;code class=&quot;language-text&quot;&gt;final&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;We also don’t have to hardcode the app name and can replace above &lt;code class=&quot;language-text&quot;&gt;&quot;MyApp&quot;&lt;/code&gt; with below &lt;code class=&quot;language-text&quot;&gt;myAppName&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; myAppName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// XCUIApplication().path example:&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// &quot;/Users/{name}/Library/Developer/Xcode/DerivedData/MyApp-{someID}/Build/Products/Debug-iphonesimulator/MyApp.app&quot;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; appName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;XCUIApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;path
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;separatedBy&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;last&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;separatedBy&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dropLast&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;appName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally, verify everything works thus far by calling &lt;code class=&quot;language-text&quot;&gt;Springboard.deleteMyApp()&lt;/code&gt; in, say, test class’s &lt;code class=&quot;language-text&quot;&gt;setUp()&lt;/code&gt;. Here’s how it should look like:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/28c63c655efdbde8673c21cd45e72c3c/springboard_automate_delete.gif&quot; alt=&quot;SpringBoard automated app delete&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;permissions-testing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#permissions-testing&quot; aria-label=&quot;permissions testing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Permissions testing&lt;/h2&gt;
&lt;p&gt;We now have all prerequisites for permissions testing. With the private &lt;code class=&quot;language-text&quot;&gt;XCUIApplication(privateWithPath:bundleID:)&lt;/code&gt; constructor at our disposal, we call it with &lt;code class=&quot;language-text&quot;&gt;&quot;com.apple.Preferences&quot;&lt;/code&gt; as the &lt;code class=&quot;language-text&quot;&gt;bundleID:&lt;/code&gt;. It returns us a handle on Settings.app, and with it we get to reproducibly set our app’s notification settings and then verify that the app responds accordingly.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight has-highlighted-lines&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Springboard&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; springboard &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;XCUIApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;privateWithPath&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bundleID&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;com.apple.springboard&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; settings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;XCUIApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;privateWithPath&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bundleID&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;com.apple.Preferences&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; settingsIcon &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; springboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;icons&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Settings&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;First add a helper function:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;openSettings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Make sure home screen is frontmost&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;XCUIApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;terminate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Go to SpringBoard&apos;s first screen&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;XCUIDevice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shared&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;press&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;home&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;forTimeInterval&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;XCTAssert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;settingsIcon&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isHittable&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Launch Settings.app from a known state&lt;/span&gt;
  settings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;terminate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  settingsIcon&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally, notifications. These are slightly more tricky, as, besides notifications being ON or OFF, we also have to consider the 3rd state, when the app hasn’t yet requested notifications permissions and thus might not be listed in Settings. Taking this aside for now, here’s how to turn notifications ON or OFF:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NotificationsState&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; on&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; off
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;turnNotifications&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; desiredState&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NotificationsState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;openMyAppSectionInSettings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  settingsTable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cells&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;staticTexts&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Notifications&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; currentState&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NotificationsState&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; soundsCell &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; settingsTable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cells&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;staticTexts&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Sounds&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; soundsCell&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exists &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; soundsCell&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isHittable
      &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on
      &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;desiredState &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; currentState &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;desiredState &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; currentState &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; allowNotificationsSwitch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; settings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;switches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;boundBy&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    allowNotificationsSwitch&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;See &lt;a href=&quot;https://github.com/b1az/Samples/tree/master/iOSAutomatedPermissionsTesting&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;the sample project&lt;/a&gt; which puts the above together. Also note that not all permissions require deletion of the app; e.g. location permissions can be reset in Settings &gt; General:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;resetLocationAndPrivacy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;openSettings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  settings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tables&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;staticTexts&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;General&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  settings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tables&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;staticTexts&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Reset&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  settings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tables&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;staticTexts&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Reset Location &amp;amp; Privacy&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  settings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;buttons&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Reset Warnings&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  settings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;terminate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;but-wait-theres-more&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#but-wait-theres-more&quot; aria-label=&quot;but wait theres more permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;But Wait, There’s More!&lt;/h2&gt;
&lt;p&gt;Not only Settings, we can interact with other Apple’s apps as well. Below are some of the bundle IDs.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;App Name	| Bundle ID
---------------------------------
App Store	| com.apple.AppStore
Calculator	| com.apple.calculator
Calendar	| com.apple.mobilecal
Camera		| com.apple.camera
Clock		| com.apple.mobiletimer
Compass		| com.apple.compass
Contacts	| com.apple.MobileAddressBook
FaceTime	| com.apple.facetime
Files       | com.apple.DocumentManager # iOS 11+
iCloud Drive| com.apple.iclouddriveapp  # iOS 10
Find Friends| com.apple.mobileme.fmf1
Find iPhone	| com.apple.mobileme.fmip1
Health		| com.apple.health
Home		| com.apple.home
iBooks		| com.apple.ibooks
iTunes Store| com.apple.mobilestore
Mail		| com.apple.mobilemail
Maps		| com.apple.Maps
Messages	| com.apple.mobilesms
Music		| com.apple.Music
News		| com.apple.news
Notes		| com.apple.mobilenotes
Phone		| com.apple.mobilephone
Photos		| com.apple.mobileslideshow
Podcasts	| com.apple.podcasts
Reminders	| com.apple.reminders
Safari		| com.apple.mobilesafari
Settings	| com.apple.Preferences
Siri        | com.apple.SiriViewService
Stocks		| com.apple.stocks
Tips		| com.apple.tips
TV			| com.apple.tv
Videos		| com.apple.videos
Voice Memos	| com.apple.voicememos
Wallet		| com.apple.passbook
Watch		| com.apple.bridge
Weather		| com.apple.weather&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Automated deeplinks testing]]></title><description><![CDATA[Automatically verify your app's deeplinks on iOS 11+ with a new XCUIApplication constructor.]]></description><link>https://merela.org/blog/automated-deeplinks-testing</link><guid isPermaLink="false">https://merela.org/blog/automated-deeplinks-testing</guid><pubDate>Mon, 28 Aug 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;UI testing under iOS 11 has the ability to interact with other installed apps. Specifically, &lt;code class=&quot;language-text&quot;&gt;XCUIApplication&lt;/code&gt; got a new constructor that takes a bundle ID. Let’s see how we use this to put our deeplinks under automated tests.&lt;/p&gt;
&lt;h2 id=&quot;overview&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#overview&quot; aria-label=&quot;overview permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Overview&lt;/h2&gt;
&lt;p&gt;What you basically do is create a helper-app, &lt;code class=&quot;language-text&quot;&gt;MyHelperApp&lt;/code&gt; in below examples, that merely holds UIButtons for all the deeplinks your main app, &lt;code class=&quot;language-text&quot;&gt;MyApp&lt;/code&gt;, should support. For &lt;code class=&quot;language-text&quot;&gt;MyApp&lt;/code&gt; you register a URL scheme, and for &lt;code class=&quot;language-text&quot;&gt;HelperApp&lt;/code&gt; you whitelist that very same scheme under the &lt;code class=&quot;language-text&quot;&gt;LSApplicationQueriesSchemes&lt;/code&gt; key in &lt;code class=&quot;language-text&quot;&gt;Info.plist&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 22.285714285714285%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAv0lEQVQI1z2MgW6DMAxE+f9fm9at2rQWSiugDNJspCSxnTmh7lImzbJPz9LdFaMh731KCRGzOue01vNslboopQCJk0SOHiCEEFMCxPDDMT6gcED7VoF3+ReRvzw4C24mcEvk+/+I5Hus3Fa4F7n+VFfnvs9grZXVZnDZHPT29PVS66f9UI1UjfDWmN0n1hcuB2iMdNdbEZlDQO+BiJhZ1voJltfDuK2Ho/KNpm6K3RQ+2u/d+dpOvCn753J4b80vl3fhSP39cSEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/3c792b1f1cd410fc4669e305646dd2de/c54d4/LSQueriesSchemes.webp 175w,
/static/3c792b1f1cd410fc4669e305646dd2de/a3432/LSQueriesSchemes.webp 350w,
/static/3c792b1f1cd410fc4669e305646dd2de/426ac/LSQueriesSchemes.webp 700w,
/static/3c792b1f1cd410fc4669e305646dd2de/c139f/LSQueriesSchemes.webp 1050w,
/static/3c792b1f1cd410fc4669e305646dd2de/7f403/LSQueriesSchemes.webp 1400w,
/static/3c792b1f1cd410fc4669e305646dd2de/57a9a/LSQueriesSchemes.webp 1439w&quot;
              sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/3c792b1f1cd410fc4669e305646dd2de/4edbd/LSQueriesSchemes.png 175w,
/static/3c792b1f1cd410fc4669e305646dd2de/13ae7/LSQueriesSchemes.png 350w,
/static/3c792b1f1cd410fc4669e305646dd2de/8c557/LSQueriesSchemes.png 700w,
/static/3c792b1f1cd410fc4669e305646dd2de/e996b/LSQueriesSchemes.png 1050w,
/static/3c792b1f1cd410fc4669e305646dd2de/2cefc/LSQueriesSchemes.png 1400w,
/static/3c792b1f1cd410fc4669e305646dd2de/73a2e/LSQueriesSchemes.png 1439w&quot;
            sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/3c792b1f1cd410fc4669e305646dd2de/8c557/LSQueriesSchemes.png&quot;
            alt=&quot;MyApp whitelisted in HelperApp&quot;
            title=&quot;MyApp whitelisted in HelperApp&quot;
            loading=&quot;lazy&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The new &lt;code class=&quot;language-text&quot;&gt;XCUIApplication&lt;/code&gt; constructor allows you to open &lt;code class=&quot;language-text&quot;&gt;MyHelperApp&lt;/code&gt;…&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; helperApp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;XCUIApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bundleIdentifier&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;org.merela.MyHelperApp&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
helperApp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;activate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
helperApp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;buttons&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;deeplink&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;…and there open a particular deeplink that &lt;code class=&quot;language-text&quot;&gt;MyApp&lt;/code&gt; supports:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;myAppBundleId&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;://&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;buttonTitle&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;UIApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally, assert that the deeplink opened &lt;code class=&quot;language-text&quot;&gt;MyApp&lt;/code&gt; in the expected state.&lt;/p&gt;
&lt;p&gt;With Xcode 9, check out &lt;a href=&quot;https://github.com/b1az/Samples/tree/master/iOSAutomatedDeeplinksTesting&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;the sample project&lt;/a&gt;. To try it out, first install the &lt;code class=&quot;language-text&quot;&gt;HelperApp&lt;/code&gt; and then run tests for the &lt;code class=&quot;language-text&quot;&gt;MyApp&lt;/code&gt; target. Also &lt;a href=&quot;ios-potpourri.html&quot;&gt;make sure Simulator’s ‘Hardware Keyboard’ is disabled&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;ci-considerations&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ci-considerations&quot; aria-label=&quot;ci considerations permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CI considerations&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Xcode 9 GM will be released soon, but until then you have to switch to Xcode-beta’s toolchain, to be used by &lt;code class=&quot;language-text&quot;&gt;xcodebuild&lt;/code&gt; commands further below:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; xcode-select &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; /Applications/Xcode-beta.app/Contents/Developer&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/blockquote&gt;
&lt;p&gt;This proof-of-concept script will make sure helper-app is always installed and ready to be used by main-app’s UI tests:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;HELPER_APP&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;MyHelperApp&apos;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;MAIN_APP&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;MyApp&apos;&lt;/span&gt;

&lt;span class=&quot;token assign-left variable&quot;&gt;BUILD_DIR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/tmp/&lt;span class=&quot;token variable&quot;&gt;$HELPER_APP&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;DESTINATION&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;platform=iOS Simulator,name=iPhone 7 Plus,OS=11.0&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Build an .app for the helper-app&lt;/span&gt;
xcodebuild build                       &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
           &lt;span class=&quot;token parameter variable&quot;&gt;-destination&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$DESTINATION&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
           &lt;span class=&quot;token parameter variable&quot;&gt;-target&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$HELPER_APP&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
           &lt;span class=&quot;token parameter variable&quot;&gt;-scheme&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$HELPER_APP&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
           &lt;span class=&quot;token assign-left variable&quot;&gt;CONFIGURATION_BUILD_DIR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$BUILD_DIR&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Install the helper-app onto the Simulator&lt;/span&gt;
xcrun simctl &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; booted &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$BUILD_DIR&lt;/span&gt;/&lt;span class=&quot;token variable&quot;&gt;$HELPER_APP&lt;/span&gt;.app&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# UI test the main-app&lt;/span&gt;
xcodebuild &lt;span class=&quot;token builtin class-name&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-scheme&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$MAIN_APP&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-destination&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$DESTINATION&lt;/span&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;One other new API worth mentioning is &lt;code class=&quot;language-text&quot;&gt;XCTContext.runActivity(named:block:)&lt;/code&gt;, which logically groups subparts of each test-case, makes these groups visible in logs, and thus eases debugging:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 644px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 37.714285714285715%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABXklEQVQoz5VQyUoDQRCdn4roQQ8KxiARRUn8pCguGI1L4p/4B4I3BQ+R2bqnZ+3xFJwleXZVnLPa8KjqrtevXpX15D2jKr7w5iR4dTXyLEWSJEjTFEVRgM5isfgzrJf4HZjX+Ag+WTRNYoRhyJjNZijLkkHiv+V1XcNiB0sfUNJHFEWQUiKOYwRBAKUUsiyDbdt8z/McQgiuNxyqTadTNmDV8zlbLasKMlBGMDYCmokEaqB1bnLFqyAxpUJuRIJUT9MMqWkahhGsZvbKWI7MmESi/TXuSCTXmmtam08/Qo0YrYb42nCIyyObJTJZmu6BFByVCuB7LuZmv/85VmvgYvXEwcG9j87QQfvKMdFFd+Rh68LmuH21RHfkoz300Ln2sGNA+f6dj7VTB62BjRUDa/3Mxca5i71bH4cPAkdjieNHif5EYtcI9Cje+FynN+L0J8IYEPzeGwtsXrpodL4BBr83n6njnSoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/5cec5eb2b37c6ab1b872ee2b14c153a0/c54d4/context.webp 175w,
/static/5cec5eb2b37c6ab1b872ee2b14c153a0/a3432/context.webp 350w,
/static/5cec5eb2b37c6ab1b872ee2b14c153a0/f847d/context.webp 644w&quot;
              sizes=&quot;(max-width: 644px) 100vw, 644px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/5cec5eb2b37c6ab1b872ee2b14c153a0/4edbd/context.png 175w,
/static/5cec5eb2b37c6ab1b872ee2b14c153a0/13ae7/context.png 350w,
/static/5cec5eb2b37c6ab1b872ee2b14c153a0/78274/context.png 644w&quot;
            sizes=&quot;(max-width: 644px) 100vw, 644px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/5cec5eb2b37c6ab1b872ee2b14c153a0/78274/context.png&quot;
            alt=&quot;runActivity context&quot;
            title=&quot;runActivity context&quot;
            loading=&quot;lazy&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;There’s plenty more: performance improvements (&lt;code class=&quot;language-text&quot;&gt;firstMatch&lt;/code&gt;), screenshots, attachments, etc. Check out WWDC 2017’s &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2017/409/&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;What’s New in Testing&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Distilled iOS development tips]]></title><description><![CDATA[A disparate collection of iOS development tips.]]></description><link>https://merela.org/blog/ios-development-distilled</link><guid isPermaLink="false">https://merela.org/blog/ios-development-distilled</guid><pubDate>Tue, 20 Jun 2017 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;how-to-get-more-accurate-xcode-test-coverage&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-to-get-more-accurate-xcode-test-coverage&quot; aria-label=&quot;how to get more accurate xcode test coverage permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How to get more accurate Xcode test coverage&lt;/h2&gt;
&lt;p&gt;A next step from testing your code is knowing how much of your code is covered by these tests. To do that, you have to first open the &lt;code class=&quot;language-text&quot;&gt;Edit Scheme&lt;/code&gt; and tell Xcode to &lt;code class=&quot;language-text&quot;&gt;Gather coverage data&lt;/code&gt;. (This setting is as of Xcode 8.2.1 disabled by default.)
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.42857142857143%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABYlAAAWJQFJUiTwAAABb0lEQVQoz22SCW7jMAxFff/T9A4zRxh0ZpLaStRY8ZJ43yX5VVbrIgj6DZqQBD5KJINlWZjnhU8/E5cD/Tij9YJSCiEEUkq6rsNa6/eiKOJ0OlEUBcYYHhmB1pp1Xb9t0hZjVx/c9z1t23rYNE0+eFuXZUnTNAzD4Pc2BjiO1QSrC06TlDzPvyzzfhoH2rokTVP2pFuSXdt6hxmjefmjSVvrgO5A5hKhBGEccpBHwssReU94VQlv8oAqFFmTkdYpWZ2hKkUxFuD4xhqf6DW1dDMEdrWUS8l7+o6IXb2cD8//+Rtf+X1ukSriksX+PLq42qkT4iq4j/fPm7rvUcH206t2ySxVX3EQR47iH2/JjV+iQbgbn5V0diaUIfIqGcyAtvr76b4cX94Dy6Ikcc+75TdveZbTNp3rnEujDfO0sNX6WRvgWR44jiNVVflu1nXtOzlNoxuD2XdyG4etTo/TsMOeocFPWXZtkOfO/uQf9QGEFrbMXAnhGwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/4a89ff116919a8f2fa81aa8d0d8d35d2/c54d4/GatherCoverageData.webp 175w,
/static/4a89ff116919a8f2fa81aa8d0d8d35d2/a3432/GatherCoverageData.webp 350w,
/static/4a89ff116919a8f2fa81aa8d0d8d35d2/426ac/GatherCoverageData.webp 700w,
/static/4a89ff116919a8f2fa81aa8d0d8d35d2/9b58d/GatherCoverageData.webp 774w&quot;
              sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/4a89ff116919a8f2fa81aa8d0d8d35d2/4edbd/GatherCoverageData.png 175w,
/static/4a89ff116919a8f2fa81aa8d0d8d35d2/13ae7/GatherCoverageData.png 350w,
/static/4a89ff116919a8f2fa81aa8d0d8d35d2/8c557/GatherCoverageData.png 700w,
/static/4a89ff116919a8f2fa81aa8d0d8d35d2/41d3b/GatherCoverageData.png 774w&quot;
            sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/4a89ff116919a8f2fa81aa8d0d8d35d2/8c557/GatherCoverageData.png&quot;
            alt=&quot;Gather coverage data setting in Xcode&quot;
            title=&quot;Gather coverage data setting in Xcode&quot;
            loading=&quot;lazy&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Running unit tests as is would give you inaccurate coverage, as it would take into account &lt;code class=&quot;language-text&quot;&gt;AppDelegate&lt;/code&gt; and everything it touches. (Because app process hosts these unit tests and they &lt;a href=&quot;https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/testing_with_xcode/chapters/05-running_tests.html#//apple_ref/doc/uid/TP40014132-CH5-SW4&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;are executed after receiving notification for &lt;code class=&quot;language-text&quot;&gt;UIApplicationDidFinishLaunching&lt;/code&gt;&lt;/a&gt;.) This is easily seen on a new project, using the &lt;code class=&quot;language-text&quot;&gt;Single View Application&lt;/code&gt; template that comes with &lt;code class=&quot;language-text&quot;&gt;AppDelegate.swift&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;ViewController.swift&lt;/code&gt; files. As you see below, I’ve also added &lt;code class=&quot;language-text&quot;&gt;AnIntegration.swift&lt;/code&gt; that contains only one empty function which gets called from &lt;code class=&quot;language-text&quot;&gt;AppDelegate&lt;/code&gt; and so, by definition, has 100% coverage:
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAABYlAAAWJQFJUiTwAAABQklEQVQoz01Ri26DMAzk/7+u0qRVm1q08RiFhJAESp4Ou8DYdoqii2Wfz05hrfUnQgjLsnRdB259GLR7KGddCN4jaIwJmf6hwDulFHcQEbSEECtgrNBPrhZjHRQ5585BJuNIBikgALbtgApuRJAqpZzEyIZeaz3PM27v3JaIYkQChHJn9BnH8ZAAjvr/+I3YQE9PxpNSqu973IWeNedDVVVN00DoGAH+QejEXp++Znpl9Cnh+NhPKKxzBqamCSsZhoExBlUMmXvmrokSxUR4jWuqZKpVetqQTYe4Fy+zHHldN3VdQwLFGCT333uf9qmb6cpIwjq+wtkYXGGsXbWaOPusqrIs27aFf+wD6Tdxu7LrXdwvj8vHVLZqe3nQO483Ht9YrGUswj4hdvjzA+fa8vAUffQ22jWsIBg9UPJxc/sB+QbX3Qaq4ZPw3AAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/119a47dde84e845ee6d9cf18863283f4/c54d4/CoverageData_before.webp 175w,
/static/119a47dde84e845ee6d9cf18863283f4/a3432/CoverageData_before.webp 350w,
/static/119a47dde84e845ee6d9cf18863283f4/426ac/CoverageData_before.webp 700w,
/static/119a47dde84e845ee6d9cf18863283f4/8af20/CoverageData_before.webp 884w&quot;
              sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/119a47dde84e845ee6d9cf18863283f4/4edbd/CoverageData_before.png 175w,
/static/119a47dde84e845ee6d9cf18863283f4/13ae7/CoverageData_before.png 350w,
/static/119a47dde84e845ee6d9cf18863283f4/8c557/CoverageData_before.png 700w,
/static/119a47dde84e845ee6d9cf18863283f4/136a2/CoverageData_before.png 884w&quot;
            sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/119a47dde84e845ee6d9cf18863283f4/8c557/CoverageData_before.png&quot;
            alt=&quot;Inaccurate coverage&quot;
            title=&quot;Inaccurate coverage&quot;
            loading=&quot;lazy&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To get accurate code coverage first add a &lt;code class=&quot;language-text&quot;&gt;AppDelegate&lt;/code&gt; barebones replica to your test-target:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIKit&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TestAppDelegate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIResponder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIApplicationDelegate&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIWindow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token omit keyword&quot;&gt;_&lt;/span&gt; application&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; didFinishLaunchingWithOptions launchOptions&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UIApplicationLaunchOptionsKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then add &lt;code class=&quot;language-text&quot;&gt;main.swift&lt;/code&gt; to the app-target in and finally tell Xcode to, when testing, replace &lt;code class=&quot;language-text&quot;&gt;AppDelegate&lt;/code&gt; with the above &lt;code class=&quot;language-text&quot;&gt;TestAppDelegate&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foundation&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIKit&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; argc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CommandLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;argc
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; argv &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UnsafeMutableRawPointer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CommandLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;unsafeArgv&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bindMemory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;to&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UnsafeMutablePointer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Int8&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; capacity&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;argc&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; appDelegateClass&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnyClass&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProcessInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isUnitTesting
  &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSClassFromString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;MyAppTests.TestAppDelegate&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AppDelegate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;UIApplicationMain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;argc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; argv&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSStringFromClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;appDelegateClass&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;ProcessInfo.isUnitTesting&lt;/code&gt; above is an extension on &lt;code class=&quot;language-text&quot;&gt;ProcessInfo&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foundation&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProcessInfo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; isUnitTesting&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; processInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;environment&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;XCTestConfigurationFilePath&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token nil constant&quot;&gt;nil&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Since you supplied &lt;code class=&quot;language-text&quot;&gt;main.swift&lt;/code&gt; file, you now also have to remove the &lt;code class=&quot;language-text&quot;&gt;@UIApplicationMain&lt;/code&gt; &lt;a href=&quot;https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Attributes.html&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;attribute&lt;/a&gt; that’s right above your &lt;code class=&quot;language-text&quot;&gt;AppDelegate&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight has-highlighted-lines&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@UIApplicationMain&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// remove this&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AppDelegate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIResponder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIApplicationDelegate&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally, run the tests again and coverage of &lt;code class=&quot;language-text&quot;&gt;AnIntegration&lt;/code&gt; is now at the actual value of 0%:
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 49.142857142857146%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAABYlAAAWJQFJUiTwAAABcElEQVQoz42Sy3KDMAxF+f9vyyqLdNpNUxICxeYd8JtbSZnQNO2imrmYke1jXdmZtRbe+00xRkzThNPphBA8rI8oB4eCxP/eOZkzxtB8+LGXlTGAg0dWSkkWMnQYBhnbfoBuO1yvs+S6roMlMK9j3ffxmPFJrMdY11VO6/tegCNB2rbFPM9YlkVyMThax5C05ZiTtXSy0s2WYNB/YjAEcgEpeozjiKZpyMEV2USftlEozmfpjdYajuxwb9kOV8t2vrWShYTLmFCNAVfj4HzY7GeOKrIEncheWVWo6xoVjWVZiuV7Cx5FGTRzwnu3Iu8TjPVyWQI0VMkyjei0wkee43g8CkwpJc3nRj8DWc55aRE7WcxNfFECNAxUNYqiEBhbZnv3zTHRc6FesRwprZGeUcKhjnjVN73QP1e7VTjQLZ4JmFOVbJnhDObQs8b+c4+DOmB32eGteUMKEc7SJTq7KXiq8LGSX7Hiz7zkZO5ZK74AJ64IhboNyE0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/6d32605016caa8a65990c8105af622c4/c54d4/CoverageData_after.webp 175w,
/static/6d32605016caa8a65990c8105af622c4/a3432/CoverageData_after.webp 350w,
/static/6d32605016caa8a65990c8105af622c4/426ac/CoverageData_after.webp 700w,
/static/6d32605016caa8a65990c8105af622c4/8af20/CoverageData_after.webp 884w&quot;
              sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/6d32605016caa8a65990c8105af622c4/4edbd/CoverageData_after.png 175w,
/static/6d32605016caa8a65990c8105af622c4/13ae7/CoverageData_after.png 350w,
/static/6d32605016caa8a65990c8105af622c4/8c557/CoverageData_after.png 700w,
/static/6d32605016caa8a65990c8105af622c4/136a2/CoverageData_after.png 884w&quot;
            sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/6d32605016caa8a65990c8105af622c4/8c557/CoverageData_after.png&quot;
            alt=&quot;Accurate coverage with a test AppDelegate&quot;
            title=&quot;Accurate coverage with a test AppDelegate&quot;
            loading=&quot;lazy&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;how-to-make-xcode-downloads-robuster&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-to-make-xcode-downloads-robuster&quot; aria-label=&quot;how to make xcode downloads robuster permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How to make Xcode downloads robuster&lt;/h2&gt;
&lt;p&gt;This comes handy especially when on an unreliable network. Sign in to AppStoreConnect, open Chrome DevTools, and click the download link. The last request you’ll see in the DevTools’ Network tab will be the download request you just made. Right click on it, choose ‘Save as cURL’, and wrap the copied request in a loop:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;((&lt;/span&gt;i&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;i&lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;https://download.developer.apple.com/Developer_Tools/Xcode_12_beta_3/Xcode_12_beta_3.xip&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;authority: download.developer.apple.com&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;pragma: no-cache&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;cache-control: no-cache&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;upgrade-insecure-requests: 1&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;user-agent: {your-agent}&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;sec-fetch-site: same-site&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;sec-fetch-mode: navigate&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;sec-fetch-user: ?1&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;sec-fetch-dest: document&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;referer: https://developer.apple.com/download/&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;accept-language: en-US,en;q=0.9,sl;q=0.8,de;q=0.7&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;cookie: {your-cookie}&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--compressed&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; Xcode-beta.xip &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-L&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;  --continue-at -&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;done&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;{your-cookie}&lt;/code&gt; part should be filled in for you. The cookie can be also be fetched through other means, from Chrome DevTools or a Chrome extension. The important part above is the highlighted &lt;code class=&quot;language-text&quot;&gt;--continue-at&lt;/code&gt; flag. It instructs &lt;code class=&quot;language-text&quot;&gt;curl&lt;/code&gt; to remember the offset of already-transferred bytes and, if needed, resume the transfer of the same file from this offset.&lt;/p&gt;
&lt;h2 id=&quot;how-to-streamline-xcode-upgrades&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-to-streamline-xcode-upgrades&quot; aria-label=&quot;how to streamline xcode upgrades permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How to streamline Xcode upgrades&lt;/h2&gt;
&lt;p&gt;It seems unavoidable for new Xcode versions to override any custom keybindings. Ditto for a custom, brighter text-cursor, which is pretty much a necessity when using a darker background. And let’s not forget the plugins. Since Xcode updates aren’t that rare, here is a script of all the steps:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;update_xcode &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# Change if not using default naming, &quot;Xcode&quot; and &quot;Xcode-beta&quot;&lt;/span&gt;
  _update_xcode &lt;span class=&quot;token string&quot;&gt;&quot;Xcode&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/Applications/Xcode-beta.app/&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; _update_xcode &lt;span class=&quot;token string&quot;&gt;&quot;Xcode-beta&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

_update_xcode &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token assign-left variable&quot;&gt;XCODE_PATH&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/Applications/&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;.app&quot;&lt;/span&gt;
  &lt;span class=&quot;token assign-left variable&quot;&gt;PLUGINS_PATH&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token environment constant&quot;&gt;$HOME&lt;/span&gt;/Library/Application Support/Developer/Shared/Xcode/Plug-ins&quot;&lt;/span&gt;
  &lt;span class=&quot;token assign-left variable&quot;&gt;SOURCE_PATH&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token environment constant&quot;&gt;$HOME&lt;/span&gt;/path/to/your/keybindings_etc&quot;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;# Update plugin UUIDs&lt;/span&gt;
  &lt;span class=&quot;token assign-left variable&quot;&gt;uuid&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;defaults &lt;span class=&quot;token builtin class-name&quot;&gt;read&lt;/span&gt; $XCODE_PATH/Contents/Info DVTPlugInCompatibilityUUID&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$PLUGINS_PATH&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-name&lt;/span&gt; Info.plist &lt;span class=&quot;token parameter variable&quot;&gt;-maxdepth&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;xargs&lt;/span&gt; -I&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; defaults &lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; DVTPlugInCompatibilityUUIDs -array-add &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$uuid&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# Restore keybindings&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cp&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$SOURCE_PATH&lt;/span&gt;/IDETextKeyBindingSet.plist &lt;span class=&quot;token variable&quot;&gt;$XCODE_PATH&lt;/span&gt;/Contents/Frameworks/IDEKit.framework/Versions/A/Resources
  &lt;span class=&quot;token comment&quot;&gt;# Restore white text-cursor&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cp&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$SOURCE_PATH&lt;/span&gt;/DVTIbeamCursor.tiff &lt;span class=&quot;token variable&quot;&gt;$XCODE_PATH&lt;/span&gt;/Contents/SharedFrameworks/DVTKit.framework/Versions/A/Resources/

  restart &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

restart &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  osascript &lt;span class=&quot;token parameter variable&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;quit app &quot;$1&quot;&apos;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;xcode-8-update&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#xcode-8-update&quot; aria-label=&quot;xcode 8 update permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Xcode 8+ update&lt;/h3&gt;
&lt;p&gt;Apple hardened Xcode 8 and later against &lt;a href=&quot;https://en.wikipedia.org/wiki/XcodeGhost&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;XcodeGhost&lt;/a&gt; and thus broke many plugins. If you value your plugins over safety, then you’ll have to re-sign Xcode:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sudo codesign -f -s &quot;Some certificate e.g. &apos;iPhone Developer: Your Name (AAAAAAAA)&apos;&quot; /Applications/Xcode.app&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Not ideal, but until Xcode supports more than mere text-extensions, I think the tradeoff is worth it.&lt;/p&gt;
&lt;h2 id=&quot;how-to-use-a-git-hook-to-avoid-pushing-xcodes-mess-onto-others&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-to-use-a-git-hook-to-avoid-pushing-xcodes-mess-onto-others&quot; aria-label=&quot;how to use a git hook to avoid pushing xcodes mess onto others permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How to use a git hook to avoid pushing Xcode’s mess onto others&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://softwareengineering.stackexchange.com/questions/244807/reconciling-the-boy-scout-rule-and-opportunistic-refactoring-with-code-reviews&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;A good boy scout&lt;/a&gt; “leaves the campground cleaner than he found it”. Or at least not leave the campground &lt;em&gt;messier&lt;/em&gt; than he found it. In iOS this includes those ‘misplaced views’ warnings that Xcode sometimes unhelpfully causes when you merely &lt;em&gt;peek&lt;/em&gt; at &lt;code class=&quot;language-text&quot;&gt;.xib&lt;/code&gt; files.&lt;/p&gt;
&lt;p&gt;Future Xcode releases will hopefully fix this. But for now it’s still up to us to fix it. The following Git hook ensures all team members keep the campground clean for others:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-e&lt;/span&gt;

&lt;span class=&quot;token assign-left variable&quot;&gt;failed&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;

&lt;span class=&quot;token assign-left variable&quot;&gt;misplaced_pattern&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;misplaced=&quot;YES&quot;&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; diff-index &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-M&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--cached&lt;/span&gt; HEAD -- &lt;span class=&quot;token string&quot;&gt;&apos;*.xib&apos;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;*.storyboard&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;^+&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;egrep&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$misplaced_pattern&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;/dev/null &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;1&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PUSH REJECTED, you have misplaced views:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; --no-pager &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-l&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--cached&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$misplaced_pattern&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;*.xib&apos;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;*.storyboard&apos;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
  &lt;span class=&quot;token assign-left variable&quot;&gt;failed&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Potentially more checks that raise the &apos;failed&apos; flag.&lt;/span&gt;

&lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$failed&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;how-to-scrape-ios-url-schemas&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-to-scrape-ios-url-schemas&quot; aria-label=&quot;how to scrape ios url schemas permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How to scrape iOS URL schemas&lt;/h2&gt;
&lt;p&gt;Say you have to find out URL schemas of 3rd party iOS apps, many of them. First download these apps using the macOS iTunes. Right-click one of the downloaded apps and select &lt;code class=&quot;language-text&quot;&gt;Show in Finder&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.42857142857142%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABYlAAAWJQFJUiTwAAAB40lEQVQoz11Ry27TUBD1V/IPSGy7ZMGGHWXDogKlLEA8JASEZ1VQi7pAmEer2FEc0xrHjh+JGz/idxx7Dvc6cYUY6Vz53vE5M3NGMFQJijaELHPIUFUVYwZrauJMdSCeRxiNFCiKAt/3Yds29MmEwcDc8+A4LizbwXzuYTabQVguYyRJAj/wEccx0jRFUZYAiL5Nanr8c400y1GtKmI5Wq8rXAU1+DeapoEwHA4xGAyg6zoMw4DHqvJfmR4/Nt8MdV22pKohkpwlTq0Q+brBZZRCHLMu/azNCxPWviRJ0DQNURQhThLiGk9HBT0aLIl3X1YVDPuEmsymqCRcfynRjVcyztwELz6JuPf2Kx58+I6KFRBc12U+OFgsFgjDkAu2XR2dx7j9foresUW9E492Dxyo06C1Yl80cOtQgerneHMk4ubeE/ReH7ejCJZlwTTN1nAumLQd8qjJnl3SWHch/zYx+mNTECW0rla4sOZ0emGhyHP4YYQfAxXeItyMzMfk4GIc3Hhu7rsvv+j+s4+k6QbqVcHI/L2mbbWNud2t2xG7C50QFw2CoK26jFPaufMc13b20P8stktpGrpicWLDl7YFz3XiQifWCWZZRkVR4u7DPu3u90lStI0BjEVbFm1F/wePv3J1nIrbSx6bAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/8a928b956a2d8f3634da52b5b58388f5/c54d4/ShowAppInFinder.webp 175w,
/static/8a928b956a2d8f3634da52b5b58388f5/a3432/ShowAppInFinder.webp 350w,
/static/8a928b956a2d8f3634da52b5b58388f5/426ac/ShowAppInFinder.webp 700w,
/static/8a928b956a2d8f3634da52b5b58388f5/c139f/ShowAppInFinder.webp 1050w,
/static/8a928b956a2d8f3634da52b5b58388f5/7f403/ShowAppInFinder.webp 1400w,
/static/8a928b956a2d8f3634da52b5b58388f5/f114c/ShowAppInFinder.webp 1684w&quot;
              sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/8a928b956a2d8f3634da52b5b58388f5/4edbd/ShowAppInFinder.png 175w,
/static/8a928b956a2d8f3634da52b5b58388f5/13ae7/ShowAppInFinder.png 350w,
/static/8a928b956a2d8f3634da52b5b58388f5/8c557/ShowAppInFinder.png 700w,
/static/8a928b956a2d8f3634da52b5b58388f5/e996b/ShowAppInFinder.png 1050w,
/static/8a928b956a2d8f3634da52b5b58388f5/2cefc/ShowAppInFinder.png 1400w,
/static/8a928b956a2d8f3634da52b5b58388f5/21e8f/ShowAppInFinder.png 1684w&quot;
            sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/8a928b956a2d8f3634da52b5b58388f5/8c557/ShowAppInFinder.png&quot;
            alt=&quot;iTunes Show .ipa in Finder&quot;
            title=&quot;iTunes Show .ipa in Finder&quot;
            loading=&quot;lazy&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This will open the folder of &lt;code class=&quot;language-text&quot;&gt;.ipa&lt;/code&gt; files at &lt;code class=&quot;language-text&quot;&gt;~/Music/iTunes/iTunes Media/Mobile Applications&lt;/code&gt; which you then copy to another folder of your choosing. In this folder you now run the following script:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Usage: $0 &amp;lt;directory of .ipa files&gt; &amp;lt;output file with (app-name URL-scheme) pairs&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token for-or-select variable&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; ./*&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;token assign-left variable&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;basename&lt;/span&gt; $file .ipa&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;unzip&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-uqd&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$dir&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token assign-left variable&quot;&gt;app_name_path&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$dir&lt;/span&gt;/Payload&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-maxdepth&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;*.app&apos;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token assign-left variable&quot;&gt;app_name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;basename&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$app_name_path&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token assign-left variable&quot;&gt;plist&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;plutil &lt;span class=&quot;token parameter variable&quot;&gt;-extract&lt;/span&gt; CFBundleURLTypes.0.CFBundleURLSchemes.0 xml1 &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; - &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$app_name_path&lt;/span&gt;/Info.plist&quot;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token assign-left variable&quot;&gt;scheme&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$plist&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$app_name&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$scheme&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;/schemas
&lt;span class=&quot;token keyword&quot;&gt;done&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;how-to-lessen-compilers-shortcoming&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-to-lessen-compilers-shortcoming&quot; aria-label=&quot;how to lessen compilers shortcoming permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How to lessen compiler’s shortcoming&lt;/h2&gt;
&lt;p&gt;One weird Swift trick to not have to come up with silly and inconsistent names for the unwrapped &lt;code class=&quot;language-text&quot;&gt;weak self&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;iMightOutliveSelf &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;weak&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; `&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;` &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;doSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Fellow developers might hate you for doing this, for it is &lt;a href=&quot;https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160118/007425.html&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;a Swift compiler bug&lt;/a&gt;. Swift does allow &lt;a href=&quot;https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;backticks to use a otherwise reserved word&lt;/a&gt; in other cases though.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; This has been &lt;a href=&quot;https://github.com/apple/swift-evolution/blob/master/proposals/0079-upgrade-self-from-weak-to-strong.md&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;fixed in Swift 4.2&lt;/a&gt; and now you can unwrap &lt;code class=&quot;language-text&quot;&gt;self&lt;/code&gt; as you would any other variable, without backticks:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight has-highlighted-lines&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;iMightOutliveSelf &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;weak&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;guard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;  &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;doSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;pre-recorded-test-data&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#pre-recorded-test-data&quot; aria-label=&quot;pre recorded test data permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Pre-recorded test data&lt;/h2&gt;
&lt;p&gt;There’s a &lt;code class=&quot;language-text&quot;&gt;Test Application Data&lt;/code&gt; setting for providing test data with which you can launch tests from a known state. Among other uses, having such known state is pretty much essential for any thorough &lt;a href=&quot;http://corporationunknown.com/blog/2014/07/10/preloading-development-data-into-your-ios-app/&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;testing of Core Data migrations&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;First go to &lt;code class=&quot;language-text&quot;&gt;Window &gt; Devices&lt;/code&gt; and download the data container from your device:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 18.285714285714285%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA0klEQVQY04XJz4sBcQAF8Pn3dm32wtm6UcZxz7ioPSiRH7Vla23NwWFQklyIi4MTVzSzX5ufgzHC4T1ftz2IV5/eq6eEfgS8OYPPKYMvGQF3VtCdEXxKmZD4mjXp/xJ4Kwj6JE/+V34GXenbFMs+8m+2gGEKzJYWp3LPlxZ2+wO3toPV7oitc+Zmf4LEtX3i6g6FMrWBg3dtjLg+ZqQ0Yrw8wUdjw0RzDXlf8V/fjRKNRZn81KA1+ihWu9TqPXxXOtBbQxb0NgLBINRwmCFVhcRHLj3KAXeFOX4hAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/cfa9cd8b7cfe1f796404ef9b953f6cbd/c54d4/xcodeTestingDownloadContainer.webp 175w,
/static/cfa9cd8b7cfe1f796404ef9b953f6cbd/a3432/xcodeTestingDownloadContainer.webp 350w,
/static/cfa9cd8b7cfe1f796404ef9b953f6cbd/426ac/xcodeTestingDownloadContainer.webp 700w,
/static/cfa9cd8b7cfe1f796404ef9b953f6cbd/c139f/xcodeTestingDownloadContainer.webp 1050w,
/static/cfa9cd8b7cfe1f796404ef9b953f6cbd/963af/xcodeTestingDownloadContainer.webp 1174w&quot;
              sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/cfa9cd8b7cfe1f796404ef9b953f6cbd/4edbd/xcodeTestingDownloadContainer.png 175w,
/static/cfa9cd8b7cfe1f796404ef9b953f6cbd/13ae7/xcodeTestingDownloadContainer.png 350w,
/static/cfa9cd8b7cfe1f796404ef9b953f6cbd/8c557/xcodeTestingDownloadContainer.png 700w,
/static/cfa9cd8b7cfe1f796404ef9b953f6cbd/e996b/xcodeTestingDownloadContainer.png 1050w,
/static/cfa9cd8b7cfe1f796404ef9b953f6cbd/04784/xcodeTestingDownloadContainer.png 1174w&quot;
            sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/cfa9cd8b7cfe1f796404ef9b953f6cbd/8c557/xcodeTestingDownloadContainer.png&quot;
            alt=&quot;Download container from the device&quot;
            title=&quot;Download container from the device&quot;
            loading=&quot;lazy&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Then open the &lt;code class=&quot;language-text&quot;&gt;Edit Scheme...&lt;/code&gt; screen and load the just download &lt;code class=&quot;language-text&quot;&gt;.xcappdata&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.28571428571429%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABYlAAAWJQFJUiTwAAABqUlEQVQ4y4VT226DMAzl//9o2ldsD31Yq4JgpUAuBHq/2csxGNFu0iJZThzn+PiSxPmWvXdsrWVrGq7rmquqYudGWxTsQwgcuiC66zpuozbOy1sXtbFO7MlX+h0BtpwXhTyEcS5930egPj70I7jnpmkmYNW4h28CAxiF0DHW/X7nx+MxSR+drWu5bQc5nU7y8Ha7Tb5ExOfzmff7/QC4WCx4uVpNkbHghHW5XMaAgdvIAlkoiIr6HQ4HThC1KHIuy1KiXq/XJ0A4oa5d11NkQGACHLi8BhZA5A+w7baaUoaoM9joY1F0j3BDqtGPlCUCCSCKCXSNBIZglOe5aGsdoRnGGFrmNa02jtPSU158U11XaBTBDyVBfQUQqeoCOxQXILvdDgEIQfbHE71/Wnr7cAy9bVq6XS8gQwA6Ho8iUsN53TTdl0X/7Z+aMgd57SANS/Wf+1+AoAyWMKK4OEPjrDbdq2AWAYA7ncEJEHOWZZk0Audys+EsTcUemyF3mFHsVc+/JWzwlRr6cVjlr+J/RmdjLNdm+MuwYaxWcfDX67UI3ui3m39BMPwBklPoJDE0BGUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/9369442845a55024465c846bd77365b8/c54d4/xcodeTestingEditScheme.webp 175w,
/static/9369442845a55024465c846bd77365b8/a3432/xcodeTestingEditScheme.webp 350w,
/static/9369442845a55024465c846bd77365b8/426ac/xcodeTestingEditScheme.webp 700w,
/static/9369442845a55024465c846bd77365b8/c139f/xcodeTestingEditScheme.webp 1050w,
/static/9369442845a55024465c846bd77365b8/7f403/xcodeTestingEditScheme.webp 1400w,
/static/9369442845a55024465c846bd77365b8/05270/xcodeTestingEditScheme.webp 1914w&quot;
              sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/9369442845a55024465c846bd77365b8/4edbd/xcodeTestingEditScheme.png 175w,
/static/9369442845a55024465c846bd77365b8/13ae7/xcodeTestingEditScheme.png 350w,
/static/9369442845a55024465c846bd77365b8/8c557/xcodeTestingEditScheme.png 700w,
/static/9369442845a55024465c846bd77365b8/e996b/xcodeTestingEditScheme.png 1050w,
/static/9369442845a55024465c846bd77365b8/2cefc/xcodeTestingEditScheme.png 1400w,
/static/9369442845a55024465c846bd77365b8/14747/xcodeTestingEditScheme.png 1914w&quot;
            sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/9369442845a55024465c846bd77365b8/8c557/xcodeTestingEditScheme.png&quot;
            alt=&quot;Edit schema&quot;
            title=&quot;Edit schema&quot;
            loading=&quot;lazy&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Note that from Xcode 6 to including Xcode 7.3 this &lt;a href=&quot;http://www.openradar.me/radar?id=4545448916287488&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;might not&lt;/a&gt; &lt;a href=&quot;https://openradar.appspot.com/21298033&quot; target=&quot;_self&quot; rel=&quot;noopener noreferrer&quot;&gt;be working on the iOS Simulator&lt;/a&gt;.&lt;/p&gt;</content:encoded></item></channel></rss>