<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>kyrofa&#39;s blog</title>
    <link>https://kyrofa.com/</link>
    <description>Recent content on kyrofa&#39;s blog</description>
    <generator>Hugo -- gohugo.io</generator>
    <lastBuildDate>Mon, 07 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://kyrofa.com/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>How to use RubyGems Trusted Publishing with Semantic Release</title>
      <link>https://kyrofa.com/posts/rubygems-trusted-publishing-with-semantic-release/</link>
      <pubDate>Mon, 07 Sep 2026 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/rubygems-trusted-publishing-with-semantic-release/</guid>
      <description>&amp;ldquo;Can I safely use this new release?&amp;rdquo;
When you&amp;rsquo;re a developer maintaining a piece of software, this is a question you ask every time you update one of its dependencies. How do you answer that question? Read the CHANGELOG? Release notes? Pray? No, you look at the version (first, at least). How to interpret that version generally depends on the project, but I&amp;rsquo;m personally partial to semantic versioning. I think it serves as an elegant contract between dependency author and consumer that can be used to easily answer this question.</description>
      <content:encoded>&lt;p&gt;&amp;ldquo;Can I safely use this new release?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;When you&amp;rsquo;re a developer maintaining a piece of software, this is a question you ask every time you update one of its dependencies. How do you answer that question? Read the CHANGELOG? Release notes? Pray? No, you look at the version (first, at least). How to interpret that version generally depends on the project, but I&amp;rsquo;m personally partial to &lt;a href=&#34;https://semver.org/&#34;&gt;semantic versioning&lt;/a&gt;. I think it serves as an elegant contract between dependency author and consumer that can be used to easily answer this question.&lt;/p&gt;
&lt;p&gt;That said, I don&amp;rsquo;t actually care about the version itself, I just care about the contract. Bugfixes bump the patch version, features bump the minor version, breaking changes bump the major version. Actually deciding when to do a release, determining the version to use, and then DOING that version bump and release is super boring. The same critique goes for the release notes. Thankfully, with a little commit discipline, both problems can be solved the same way: &lt;a href=&#34;https://www.conventionalcommits.org/en/v1.0.0/&#34;&gt;conventional commits&lt;/a&gt;. If each commit can be used to determine if a given change is a bugfix/feature/breaking change, then you can automate the rest with something like &lt;a href=&#34;https://github.com/semantic-release/semantic-release&#34;&gt;Semantic Release&lt;/a&gt;. That&amp;rsquo;s what I&amp;rsquo;ve been doing for the better part of a decade, and I can&amp;rsquo;t recommend it enough.&lt;/p&gt;
&lt;p&gt;Historically, publishing a gem to RubyGems automatically from CI was easy: you create an API token scoped to specifically publish that gem, and use that token in your CI environment to push the gem. However, that token is one of the targets for supply chain attacks. Also, using tokens like that requires that your account doesn&amp;rsquo;t utilize multi-factor authentication (MFA), which you should definitely be using. So what is the solution? &lt;a href=&#34;https://guides.rubygems.org/trusted-publishing/&#34;&gt;Trusted Publishing&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&#34;rubygems-trusted-publishing&#34;&gt;RubyGems Trusted Publishing&lt;/h1&gt;
&lt;p&gt;Trusted Publishing is pretty well documented elsewhere, so I don&amp;rsquo;t want to dwell on the details here for too long. For our purposes, let&amp;rsquo;s gain a high-level understanding of how this works by contrasting it with the API token approach mentioned above.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Without trusted publishing:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create API token, scoped as tightly as possible to just pushing this gem&lt;/li&gt;
&lt;li&gt;Put it in CI variables in as secure a manner as possible (e.g. only defined on your release branch(es), etc.)&lt;/li&gt;
&lt;li&gt;When releasing the gem, use that variable to authenticate to RubyGems, and push the gem&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Repeat (3) every time you need to release a new version. You can see how, if some part of your supply chain is compromised (e.g. your dependencies, or some part of your build system), that key can be stolen every time (3) runs. And since it&amp;rsquo;s a long-lived key, the attackers can use it until you happen to detect that it has been compromised. Yes, thanks to your carefulness in (1) the damage they can do is limited to your gem, but they can still turn your gem into an attack vector for your users by pushing malicious versions of it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;With trusted publishing:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go to your gem&amp;rsquo;s settings in RubyGems, and add a new Trusted Publisher. This is identified by your GitHub repository, GitHub Actions workflow file, and environment name. You&amp;rsquo;re basically saying &amp;ldquo;allow this gem to be published from this GitHub Action running in this environment.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;When releasing the gem (from the workflow and environment you specified in (1)), obtain a new token from RubyGems, use it to authenticate, and push the gem&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Repeat (2) every time you need to release a new version. On the surface, perhaps this doesn&amp;rsquo;t sound so different from the &amp;ldquo;without trusted publishing&amp;rdquo; flow, but it is: the token you fetch in (2) is incredibly short-lived. Even if CI is compromised, the window of opportunity for utilizing the stolen token is vanishingly small. This method of authenticating and pushing new releases is far superior.&lt;/p&gt;
&lt;h1 id=&#34;how-do-we-automatically-push-new-gem-versions-with-trusted-publishing&#34;&gt;How do we automatically push new gem versions with Trusted Publishing?&lt;/h1&gt;
&lt;p&gt;You may have noticed that step (2) above, with the &amp;ldquo;with trusted publishing&amp;rdquo; flow, was uselessly high level. I could have written &amp;ldquo;Magic happens here.&amp;rdquo; That&amp;rsquo;s because the process of actually contacting RubyGems to obtain that short-lived token is complicated enough to not really be intended to be done by you, dear developer. RubyGems kindly created a GitHub Action specifically for this, called &lt;a href=&#34;https://github.com/rubygems/release-gem&#34;&gt;release-gem&lt;/a&gt;. That is nifty, but to quote the end of the documentation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;With the trusted publisher configured and this workflow in your repository, you can release a new version of your gem by simply pushing a git tag. No API tokens, no manual &lt;code&gt;gem push&lt;/code&gt; — just tag and push.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you read the intro to this article, you&amp;rsquo;ll know that&amp;rsquo;s a problem for me. In order for me to push a tag, I have to select a version. I have to write the CHANGELOG. I have to do all the boring stuff that I don&amp;rsquo;t want to do. I want Semantic Release doing this for me! Can I make them work together?&lt;/p&gt;
&lt;h1 id=&#34;integrate-semantic-release-with-trusted-publishing&#34;&gt;Integrate Semantic Release with Trusted Publishing&lt;/h1&gt;
&lt;p&gt;First, we need to gain a better understanding of what exactly release-gem does. To glean those details we need to dig into its &lt;a href=&#34;https://github.com/rubygems/release-gem/blob/7f9650160c1a4e7989fdc9855807bdbd421d8b6b/action.yml&#34;&gt;action.yml&lt;/a&gt;, which at the time of this writing looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Release Gem&amp;#34;&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# snip...&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;runs&lt;/span&gt;:
  &lt;span style=&#34;color:#f92672&#34;&gt;using&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;composite&amp;#34;&lt;/span&gt;
  &lt;span style=&#34;color:#f92672&#34;&gt;steps&lt;/span&gt;:
    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Attribute commits to last committer&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        git config --global user.email &amp;#34;$(git log -1 --pretty=format:&amp;#39;%ae&amp;#39;)&amp;#34;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        git config --global user.name &amp;#34;$(git log -1 --pretty=format:&amp;#39;%an&amp;#39;)&amp;#34;&lt;/span&gt;        
      &lt;span style=&#34;color:#f92672&#34;&gt;shell&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;bash&lt;/span&gt;
    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Configure Git using cached credentials&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        git credential-cache --timeout=300 store &amp;lt;&amp;lt;EOF
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        protocol=https
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        host=github.com
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        username=x-access-token
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        password=${{ inputs.token }}
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        EOF
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        git config --local credential.helper &amp;#39;cache --timeout=300&amp;#39;&lt;/span&gt;        
      &lt;span style=&#34;color:#f92672&#34;&gt;shell&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;bash&lt;/span&gt;
    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Fetch tags&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;git fetch --tags --force&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;shell&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;bash&lt;/span&gt;
    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Configure trusted publishing credentials&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;if&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ inputs.setup-trusted-publisher == &amp;#39;true&amp;#39; }}&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;rubygems/configure-rubygems-credentials@dc5a8d8553e6ee01fc26761a49e99e733d17954a&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# v2.1.0&lt;/span&gt;
    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Run release rake task&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;bundle exec rake release&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;shell&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;bash&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;env&lt;/span&gt;:
        &lt;span style=&#34;color:#f92672&#34;&gt;RUBYOPT&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;${{ inputs.attestations == &amp;#39;true&amp;#39; &amp;amp;&amp;amp; format(&amp;#39;-r{0}/rubygems-attestation-patch.rb {1}&amp;#39;, github.action_path, env.RUBYOPT) || env.RUBYOPT }}&amp;#34;&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;working-directory&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ inputs.working-directory }}&lt;/span&gt;
    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Wait for release to propagate&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;if&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ inputs.await-release == &amp;#39;true&amp;#39; }}&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;gem exec rubygems-await pkg/*.gem&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;shell&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;bash&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;working-directory&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ inputs.working-directory }}&lt;/span&gt;
    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Clean up credentials&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;if&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;always()&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;git credential-cache exit || true&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;shell&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;bash&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;From this, you can see that this isn&amp;rsquo;t the GitHub Action that actually does the &amp;ldquo;get my credentials&amp;rdquo; dance: that&amp;rsquo;s &lt;a href=&#34;https://github.com/rubygems/configure-rubygems-credentials&#34;&gt;&lt;code&gt;rubygems/configure-rubygems-credentials&lt;/code&gt;&lt;/a&gt;. If you just want something to fetch credentials for you to use in your own CI workflow, &lt;em&gt;that&lt;/em&gt; is the GitHub Action you need to care about. That&amp;rsquo;s what we need in order to integrate Trusted Publishing with Semantic Release.&lt;/p&gt;
&lt;h2 id=&#34;semantic-release-configuration&#34;&gt;Semantic Release configuration&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s take a momentary detour to discuss the Semantic Release configuration file, &lt;code&gt;.releaserc.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Standard plugins, minus npm, plus rubygems&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;plugins&lt;/span&gt;:
  - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@semantic-release/commit-analyzer&amp;#34;&lt;/span&gt;
  - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@semantic-release/release-notes-generator&amp;#34;&lt;/span&gt;
  - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@semantic-release/github&amp;#34;&lt;/span&gt;
  - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@webhippie/semantic-release-rubygem&amp;#34;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;ll notice I&amp;rsquo;m using the &lt;a href=&#34;https://github.com/webhippie/semantic-release-rubygem&#34;&gt;semantic-release-rubygem&lt;/a&gt; Semantic Release plugin to actually build/push gems, and the rest are standard.&lt;/p&gt;
&lt;h2 id=&#34;ci-workflow&#34;&gt;CI Workflow&lt;/h2&gt;
&lt;p&gt;Alright, let&amp;rsquo;s put all the pieces together. Writing the CI workflow is almost exactly as if you had a long-lived token. Here&amp;rsquo;s an example deploy job:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;deploy&lt;/span&gt;:
  &lt;span style=&#34;color:#f92672&#34;&gt;runs-on&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ubuntu-latest&lt;/span&gt;
  &lt;span style=&#34;color:#f92672&#34;&gt;environment&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;rubygems&lt;/span&gt;

  &lt;span style=&#34;color:#75715e&#34;&gt;# Only deploy if running on master, our release branch in this case&lt;/span&gt;
  &lt;span style=&#34;color:#f92672&#34;&gt;if&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;github.ref == &amp;#39;refs/heads/master&amp;#39;&lt;/span&gt;

  &lt;span style=&#34;color:#f92672&#34;&gt;permissions&lt;/span&gt;:
    &lt;span style=&#34;color:#f92672&#34;&gt;id-token&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;write&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# Required for trusted publishing&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;contents&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;write&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# Required to push tags&lt;/span&gt;

  &lt;span style=&#34;color:#f92672&#34;&gt;steps&lt;/span&gt;:
    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Checkout code&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# v6.1.0&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
        &lt;span style=&#34;color:#f92672&#34;&gt;persist-credentials&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;

    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Setup node&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# v6.5.0&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
        &lt;span style=&#34;color:#f92672&#34;&gt;node-version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;26&amp;#39;&lt;/span&gt;

    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Install semantic release&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;npm install semantic-release@25 @webhippie/semantic-release-rubygem@7&lt;/span&gt;

    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Obtain RubyGems credentials&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;rubygems/configure-rubygems-credentials@dc5a8d8553e6ee01fc26761a49e99e733d17954a&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# v2.1.0&lt;/span&gt;

    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Run semantic release&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;env&lt;/span&gt;:
        &lt;span style=&#34;color:#f92672&#34;&gt;GITHUB_TOKEN&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
      &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;npx semantic-release&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. That CI workflow obtains a short-lived token from RubyGems, and then runs Semantic Release. Semantic Release will process all the commit messages that have taken place since the last release, determine first if a release should even be made, and if so, the version that should be used. Once that is determined, it will tag that commit and push it up. It will then update the gem version, build the gem, and then push it using the credentials obtained by &lt;code&gt;configure-rubygems-credentials&lt;/code&gt;.&lt;/p&gt;
&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Semantic versioning is a great way to help your users understand if they can update to your new releases without issue. Combining conventional commits and Semantic Release lets you honor that contract without the tedious work of manually picking versions and writing changelogs. Trusted Publishing eliminates the long-lived API token from that workflow entirely, but the documentation and GitHub Actions make some assumptions that aren&amp;rsquo;t entirely compatible with Semantic Release. I hope this article was helpful in smoothing over that sharp edge and showing that, with a little care, you can take advantage of the increased security offered by Trusted Publishing without sacrificing any of that release automation. The result is magical.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>New and improved way to review GitHub pull requests for snaps</title>
      <link>https://kyrofa.com/posts/new-and-improved-way-to-review-github-pull-requests-for-snaps/</link>
      <pubDate>Sat, 16 Sep 2023 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/new-and-improved-way-to-review-github-pull-requests-for-snaps/</guid>
      <description>Reviewing pull requests for snaps has been pretty terrible ever since snaps were introduced. There are a few very simple reasons:
 Building snaps locally in order to review is too much work. This is a fine place to start, but it doesn&amp;rsquo;t scale, especially if it takes any amount of time to build the snap in question (mine takes over an hour). Most CI engines use Docker. You can build snaps in docker with hacks and tweaks that sort of work sometimes, but it&amp;rsquo;s not a supported approach, and it&amp;rsquo;ll just randomly break next week.</description>
      <content:encoded>&lt;p&gt;Reviewing pull requests for snaps has been pretty terrible ever since snaps were introduced. There are a few very simple reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Building snaps locally in order to review is too much work.&lt;/strong&gt; This is a fine place to start, but it doesn&amp;rsquo;t scale, especially if it takes any amount of time to build the snap in question (mine takes over an hour).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Most CI engines use Docker.&lt;/strong&gt; You can build snaps in docker with hacks and tweaks that sort of work sometimes, but it&amp;rsquo;s not a supported approach, and it&amp;rsquo;ll just randomly break next week.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Using private tokens to access the snap store from a fork will leak the token.&lt;/strong&gt; You don&amp;rsquo;t want random people releasing snaps on your behalf. This concern is not limited to snaps, of course.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I &lt;a href=&#34;https://kyrofa.com/posts/a-better-way-to-review-github-pull-requests-for-snaps&#34;&gt;dedicated an article to this back in 2019&lt;/a&gt;, introducing a solution that worked reasonably well for a number of years, but it had some downsides as well:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;It used Docker.&lt;/strong&gt; See (2), above.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It required your own infrastructure and a GitHub app, which was difficult to configure.&lt;/strong&gt; No one wants to do that.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Ultimately, the GitHub Snap Builder fell apart for me personally because it used Docker, and Snapcraft broke my ability to build the snap I cared about (Nextcloud) in Docker. I needed a new solution, but I wasn&amp;rsquo;t in the mood to roll my own again. I bided my time, investigated, and experimented. Two years ago, I finally started down the path that ultimately led to success, and I&amp;rsquo;d like to share that with you.&lt;/p&gt;
&lt;h2 id=&#34;github-actions&#34;&gt;GitHub Actions&lt;/h2&gt;
&lt;p&gt;For a long time, the &lt;a href=&#34;https://github.com/nextcloud-snap/nextcloud-snap&#34;&gt;Nextcloud snap&lt;/a&gt; used Circle CI as our CI system (Travis CI before that). It was the only CI system for years that could actually build snaps, since it supported a special runner type that was a virtual machine instead of being a Docker container. However, in 2021, they suddenly capped CI runtimes to one hour, with no way to override. We were forced to revisit the CI landscape, and were pleased to discover that GitHub Actions, introduced in the meantime, had all the capabilities we needed. We moved to that, and have been happy users ever since.&lt;/p&gt;
&lt;p&gt;We were still in the lurch in terms of testing pull requests, though. We could build snaps in GitHub Actions, and run our tests against them, but without something like the now-defunct GitHub Snap Builder, we couldn&amp;rsquo;t upload them to the store. The Nextcloud snap is an open source project, and we receive pull requests from unknown contributors all the time. Using GitHub Actions to upload snaps to the store would leak our private tokens to those unknown contributors (see (3), above). Until 2020.&lt;/p&gt;
&lt;p&gt;On December 15th, 2020, Jaroslav Lobacevski wrote &lt;a href=&#34;https://securitylab.github.com/research/github-actions-preventing-pwn-requests/&#34;&gt;a series of posts&lt;/a&gt; about GitHub Actions security. To be honest, I don&amp;rsquo;t actually know when the critical feature was added, but this series is what introduced me to it: the &lt;code&gt;workflow_run&lt;/code&gt; trigger.&lt;/p&gt;
&lt;p&gt;Documentation has always been the Achilles heel of GitHub Actions. All they have are reference docs that are so light on detail I find myself wince-laughing half the time. I still can&amp;rsquo;t read the docs on &lt;code&gt;workflow_run&lt;/code&gt; and come away with any meaningful understanding, but Lobacevski&amp;rsquo;s blog posts convinced me it was what I needed. Let me explain by first explaining how the GitHub Snap Builder solved this problem.&lt;/p&gt;
&lt;h2 id=&#34;github-snap-builder&#34;&gt;GitHub Snap Builder&lt;/h2&gt;
&lt;p&gt;The GitHub Snap Builder obviously needs a token for it to be able to upload snaps to the snap store. It did this on pull requests from unknown contributors. How did it avoid leaking that token? By making a very clear delineation between trusted and untrusted code. It used two ephemeral containers:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;A container to build the snap.&lt;/strong&gt; This was an unprivileged container with no tokens to leak. It was responsible for building the snap from the untrusted code in the pull request.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A container to upload the snap.&lt;/strong&gt; Once the snap was built by container 1, another container was fired up with the snap store token in its environment. It then used snapcraft to upload the snap to the store. Everything running in this container is trusted, coming from a known-good source. It&amp;rsquo;s uploading an untrusted snap, yes, but it&amp;rsquo;s not running anything out of that snap to do it. This process was hard-coded, and didn&amp;rsquo;t rely on (or run) anything in the pull request, so there&amp;rsquo;s nothing for a malicous pull request to compromise.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;how-does-that-relate-to-github-actions&#34;&gt;How does that relate to GitHub Actions?&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;workflow_run&lt;/code&gt; trigger allows for the same untrusted/trusted code separation that was possible in the GitHub Snap Builder. It supports a pull request workflow like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Pull request is opened from fork &lt;code&gt;foo/nextcloud-snap&lt;/code&gt;, branch &lt;code&gt;bar&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Snap is built of the untrusted code in the pull request, and automatically tested&lt;/li&gt;
&lt;li&gt;That snap is saved as an artifact&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once that workflow finishes on that fork&amp;rsquo;s branch, it uses the &lt;code&gt;workflow_run&lt;/code&gt; trigger to fire up a &lt;em&gt;new&lt;/em&gt; workflow on the &lt;em&gt;master&lt;/em&gt; branch: trusted code. That branch has access to the snap store token, downloads the snap from the pull request workflow&amp;rsquo;s artifacts, and uploads it. Again, all without running untrusted code.&lt;/p&gt;
&lt;h2 id=&#34;show-me-how&#34;&gt;Show me how&lt;/h2&gt;
&lt;p&gt;Ultimately you will need two workflow files, one for each side of the trust boundary. In other words, you need one to run on pull requests and build snaps, and one to run on master and upload snaps. I&amp;rsquo;ll reduce this to the minimum you need to make this happen, but if you want to see a real world example, see the &amp;lsquo;test.yml&amp;rsquo; and &amp;lsquo;release-pr-snap.yml&amp;rsquo; workflows in the &lt;a href=&#34;https://github.com/nextcloud-snap/nextcloud-snap/tree/master/.github/workflows&#34;&gt;Nextcloud snap&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;generate-new-snapcraft-token&#34;&gt;Generate new snapcraft token&lt;/h3&gt;
&lt;p&gt;The first thing you need to do is generate a token so you can authenticate with snapcraft without actually needing to provide a password. This also provides an opportunity to scope access to the specific snap and actions we&amp;rsquo;ll be doing, to minimize damage in case the token DOES leak. Do that by running a command like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft export-login token_file&lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;           --snaps&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&amp;lt;snap-name&amp;gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;           --channels&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&amp;lt;channel&amp;gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;           --acls&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;package_access,package_push,package_release
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, that requires us to decide our release strategy: what channel do we use when releasing a snap built from a pull request? This is exactly what &lt;a href=&#34;https://snapcraft.io/docs/channels#heading--branches&#34;&gt;channel branches&lt;/a&gt; are for. In Nextcloud, we use &lt;code&gt;latest/beta/pr-&amp;lt;pull request id&amp;gt;&lt;/code&gt;. So the command we use to generate a token that has the ability to upload ONLY the Nextcloud snap ONLY to &lt;code&gt;latest/beta/&amp;lt;branch&amp;gt;&lt;/code&gt; (but NOT &lt;code&gt;latest/beta&lt;/code&gt; or anything else) looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft export-login token_file&lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;           --snaps&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;nextcloud&lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;           --channels&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;latest/beta/*&lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;           --acls&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;package_access,package_push,package_release
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;create-a-new-environment-snapcraft&#34;&gt;Create a new environment: snapcraft&lt;/h3&gt;
&lt;p&gt;In order to protect access to the token you just generated, create a &lt;a href=&#34;https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment&#34;&gt;new environment&lt;/a&gt; in GitHub. I called mine &amp;ldquo;snapcraft.&amp;rdquo; Add a branch protection rule to only allow access from the master branch, and add a single secret, &lt;code&gt;SNAPCRAFT_STORE_CREDENTIALS&lt;/code&gt;, set to the contents of the &lt;code&gt;token_file&lt;/code&gt; you generated in the last step.&lt;/p&gt;
&lt;h3 id=&#34;untrusted-code-build-the-snap&#34;&gt;Untrusted code: build the snap&lt;/h3&gt;
&lt;p&gt;Alright, time for the workflow files. First create the workflow that is responsible for building (and potentially testing) the snap. I called mine &lt;code&gt;.github/workflows/test.yml&lt;/code&gt;. Make it look something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Run pull request tests&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;on&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;pull_request]&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;jobs&lt;/span&gt;:
  &lt;span style=&#34;color:#f92672&#34;&gt;test&lt;/span&gt;:
    &lt;span style=&#34;color:#f92672&#34;&gt;runs-on&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ubuntu-20.04&lt;/span&gt;

    &lt;span style=&#34;color:#f92672&#34;&gt;steps&lt;/span&gt;:
      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Check out code&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/checkout@v3&lt;/span&gt;

      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Install dependencies&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;sudo apt-get update -qq &amp;amp;&amp;amp; sudo apt-get remove -qq lxd lxd-client &amp;amp;&amp;amp; sudo snap install lxd &amp;amp;&amp;amp; sudo lxd init --auto &amp;amp;&amp;amp; sudo snap install --classic snapcraft&lt;/span&gt;

      &lt;span style=&#34;color:#75715e&#34;&gt;# Using sudo here because our CI user isn&amp;#39;t a member of the lxd group&lt;/span&gt;
      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Build snap&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;sudo snapcraft --provider lxd&lt;/span&gt;

      &lt;span style=&#34;color:#75715e&#34;&gt;# Just an example, of course. Remove this if you have no real tests&lt;/span&gt;
      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Test the snap&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;./run-tests&lt;/span&gt;

      &lt;span style=&#34;color:#75715e&#34;&gt;# Upload the snap so that we can download it in our other workflow&lt;/span&gt;
      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Upload snap artifact&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/upload-artifact@v3&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
          &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;snap&lt;/span&gt;
          &lt;span style=&#34;color:#f92672&#34;&gt;path&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;*.snap&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That simply checks the code out, installs snapcraft (and lxd, which we&amp;rsquo;re using to build here for a nice clean environment), builds and tests the snap, and then saves it as an artifact.&lt;/p&gt;
&lt;h3 id=&#34;trusted-code-upload-the-snap&#34;&gt;Trusted code: upload the snap&lt;/h3&gt;
&lt;p&gt;Alright, now we&amp;rsquo;re ready to create the workflow that is responsible for uploading the snap. I called mine &lt;code&gt;.github/workflows/release-pr-snap.yml&lt;/code&gt;. Make it look something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Release snap for pull request&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;on&lt;/span&gt;:
  &lt;span style=&#34;color:#f92672&#34;&gt;workflow_run&lt;/span&gt;:
    &lt;span style=&#34;color:#75715e&#34;&gt;# This maps to the name of our workflow above&lt;/span&gt;
    &lt;span style=&#34;color:#75715e&#34;&gt;# &amp;#34;when one of these workflows finishes, trigger me&amp;#34;&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;workflows&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Run pull request tests&amp;#34;&lt;/span&gt;]
    &lt;span style=&#34;color:#f92672&#34;&gt;types&lt;/span&gt;:
      - &lt;span style=&#34;color:#ae81ff&#34;&gt;completed&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;jobs&lt;/span&gt;:
  &lt;span style=&#34;color:#f92672&#34;&gt;upload&lt;/span&gt;:
    &lt;span style=&#34;color:#f92672&#34;&gt;runs-on&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ubuntu-latest&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;environment&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;snapcraft&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# request access to the environment we created&lt;/span&gt;

    &lt;span style=&#34;color:#75715e&#34;&gt;# Only run if what triggered us was a workflow from a pull request,&lt;/span&gt;
    &lt;span style=&#34;color:#75715e&#34;&gt;# and that workflow finished successfully.&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;if&lt;/span&gt;: &amp;gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;      ${{ github.event.workflow_run.event == &amp;#39;pull_request&amp;#39; &amp;amp;&amp;amp;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;      github.event.workflow_run.conclusion == &amp;#39;success&amp;#39; }}&lt;/span&gt;      
    &lt;span style=&#34;color:#f92672&#34;&gt;steps&lt;/span&gt;:
      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Determine pull request commit hash&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/github-script@v6&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;determine-sha&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
          &lt;span style=&#34;color:#f92672&#34;&gt;result-encoding&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;string&lt;/span&gt;
          &lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            const workflowRun = await github.rest.actions.getWorkflowRun({
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              owner: context.repo.owner,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              repo: context.repo.repo,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              run_id: ${{ github.event.workflow_run.id }},
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            });
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            return workflowRun.data.head_sha;&lt;/span&gt;            

      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Set initial commit status&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/github-script@v6&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
          &lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            await github.rest.repos.createCommitStatus({
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              owner: context.repo.owner,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              repo: context.repo.repo,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              sha: &amp;#39;${{ steps.determine-sha.outputs.result }}&amp;#39;,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              context: &amp;#39;Snap Publisher&amp;#39;,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              state: &amp;#39;pending&amp;#39;,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              description: &amp;#39;Preparing environment...&amp;#39;,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            });&lt;/span&gt;            

      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Determine release channel&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/github-script@v6&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;determine-channel&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
          &lt;span style=&#34;color:#f92672&#34;&gt;result-encoding&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;string&lt;/span&gt;
          &lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            const pullRequests = await github.paginate(
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              `GET /repos/${process.env.GITHUB_REPOSITORY}/pulls`,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              {
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                owner: context.repo.owner,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                repo: context.repo.repo,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              }
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            );
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            const matchingPullRequests = pullRequests.filter(pr =&amp;gt; pr.head.sha === &amp;#39;${{ steps.determine-sha.outputs.result }}&amp;#39;);
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            const pullRequestCount = matchingPullRequests.length;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            if (pullRequestCount != 1) {
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              throw new Error(`Expected one pull request to contain commit, but got ${pullRequestCount}`);
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            }
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            return `latest/beta/pr-${matchingPullRequests[0].number}`;&lt;/span&gt;            

      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Download snap artifact&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/github-script@v6&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
          &lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;               owner: context.repo.owner,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;               repo: context.repo.repo,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;               run_id: ${{ github.event.workflow_run.id }},
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            });
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            const snapArtifacts = artifacts.data.artifacts.filter((artifact) =&amp;gt; {
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              return artifact.name == &amp;#34;snap&amp;#34;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            });
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            if (snapArtifacts.length != 1) {
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              throw new Error(`Expected one snap artifact, but got ${snapArtifacts.length}`);
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            }
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            var download = await github.rest.actions.downloadArtifact({
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;               owner: context.repo.owner,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;               repo: context.repo.repo,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;               artifact_id: snapArtifacts[0].id,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;               archive_format: &amp;#39;zip&amp;#39;,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            });
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            var fs = require(&amp;#39;fs&amp;#39;);
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            fs.writeFileSync(&amp;#39;${{github.workspace}}/snap.zip&amp;#39;, Buffer.from(download.data));&lt;/span&gt;            

      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Extract snap artifact&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;unzip snap.zip&lt;/span&gt;

      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Install snapcraft&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;sudo apt-get update -qq &amp;amp;&amp;amp; sudo snap install --classic snapcraft&lt;/span&gt;

      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Set uploading commit status&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/github-script@v6&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
          &lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            await github.rest.repos.createCommitStatus({
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              owner: context.repo.owner,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              repo: context.repo.repo,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              sha: &amp;#39;${{ steps.determine-sha.outputs.result }}&amp;#39;,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              context: &amp;#39;Snap Publisher&amp;#39;,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              state: &amp;#39;pending&amp;#39;,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              description: &amp;#39;Currently uploading/releasing snap...&amp;#39;,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            });&lt;/span&gt;            

      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Upload snap&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;env&lt;/span&gt;:
          &lt;span style=&#34;color:#f92672&#34;&gt;SNAPCRAFT_STORE_CREDENTIALS&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;snapcraft upload *.snap --release=&amp;#34;${{ steps.determine-channel.outputs.result }}&amp;#34;&lt;/span&gt;

      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Set final commit status&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/github-script@v6&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
          &lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            await github.rest.repos.createCommitStatus({
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              owner: context.repo.owner,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              repo: context.repo.repo,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              sha: &amp;#39;${{ steps.determine-sha.outputs.result }}&amp;#39;,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              context: &amp;#39;Snap Publisher&amp;#39;,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              state: &amp;#39;success&amp;#39;,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              description: &amp;#34;Snap built and released to &amp;#39;${{ steps.determine-channel.outputs.result }}&amp;#39;&amp;#34;,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            });&lt;/span&gt;            

      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Set error commit status&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/github-script@v6&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;if&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;failure()&lt;/span&gt;
        &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
          &lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            await github.rest.repos.createCommitStatus({
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              owner: context.repo.owner,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              repo: context.repo.repo,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              sha: &amp;#39;${{ steps.determine-sha.outputs.result }}&amp;#39;,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              context: &amp;#39;Snap Publisher&amp;#39;,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              state: &amp;#39;error&amp;#39;,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              description: &amp;#39;Snap failed to upload/release&amp;#39;,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            });&lt;/span&gt;            
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because this code is running on another branch than the pull request&amp;rsquo;s workflow, there don&amp;rsquo;t seem to be any primitives available to accomplish most of what we need, so we lean pretty heavily on the GitHub API. I&amp;rsquo;d be willing to bet there are premade actions out there that could simplify some of this, but I was happy enough with this that I didn&amp;rsquo;t investigate too hard.&lt;/p&gt;
&lt;p&gt;The first thing we do is take the triggering workflow run ID, and determine the commit on which that workflow ran. We need that so we can create new commit statuses, which ultimately results in feedback in the pull request&amp;rsquo;s &amp;ldquo;checks&amp;rdquo; section. Indeed, that is the second thing we do: create a new status saying that the Snap Publisher is &amp;ldquo;Preparing environment.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Then we use that commit hash again and determine the pull request that contains it. Using that information, we&amp;rsquo;re able to calculate the release channel for this snap using the pattern we discussed previously: &lt;code&gt;latest/beta/pr-&amp;lt;pull request id&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After that, we download and extract the artifact saved by the &amp;ldquo;test.yml&amp;rdquo; workflow. At that point, we install snapcraft, so we can actually upload the snap. We update the commit status again, saying we&amp;rsquo;re uploading/releasing the snap, and then do exactly that: upload the snap and release it to the calculated channel. You&amp;rsquo;ll notice that&amp;rsquo;s where we actually use the token, defining the &lt;code&gt;SNAPCRAFT_STORE_CREDENTIALS&lt;/code&gt; environment variable, which snapcraft will use to authenticate.&lt;/p&gt;
&lt;p&gt;When that&amp;rsquo;s done, we update the commit status once more saying that it&amp;rsquo;s done, and providing the channel into which it was released, like so:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/snap-publisher.png&#34; alt=&#34;Successful snap publication&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;The very last step only runs if an error occurs elsewhere in the pipeline, and it simply updates the commit status to point out the failure.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;After a few years of painful reviews, the Nextcloud snap project is once again able to more easily evaluate its pull requests. This new workflow not only makes it easier for maintainers to review, but also lowers the barrier to entry for other members of the community to help. Now we can ask for participants in a particular issue to test out new features or fixes without teaching them to do anything beyond that with which they are already familiar. This technique has certainly helped our community, and it&amp;rsquo;s my hope that the sharing of it can help yours.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Monitoring a Nextcloud snap upgrade</title>
      <link>https://kyrofa.com/posts/monitoring-a-nextcloud-snap-upgrade/</link>
      <pubDate>Sun, 06 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/monitoring-a-nextcloud-snap-upgrade/</guid>
      <description>The Nextcloud snap updates automatically. By default it might happen any time, although you can configure that. The snap provides a wonderful, user-friendly indicator that an upgrade is happening: it will become unresponsive and unvisitable until the upgrade has finished. Okay, I&amp;rsquo;m kidding, this is terrible and something we know we need to work on. My point is, there are times when your snap isn&amp;rsquo;t responding properly, and you want to know WHY.</description>
      <content:encoded>&lt;p&gt;The Nextcloud snap updates automatically. By default it might happen any time, although &lt;a href=&#34;https://snapcraft.io/docs/keeping-snaps-up-to-date&#34;&gt;you can configure that&lt;/a&gt;. The snap provides a wonderful, user-friendly indicator that an upgrade is happening: it will become unresponsive and unvisitable until the upgrade has finished. Okay, I&amp;rsquo;m kidding, this is terrible and &lt;a href=&#34;https://github.com/nextcloud-snap/nextcloud-snap/issues/2016&#34;&gt;something we know we need to work on&lt;/a&gt;. My point is, there are times when your snap isn&amp;rsquo;t responding properly, and you want to know WHY. It&amp;rsquo;s probably because an upgrade is happening, so how do you monitor it?&lt;/p&gt;
&lt;p&gt;Logs.&lt;/p&gt;
&lt;p&gt;The Nextcloud snap has several services contained within it. There are four that are most important for upgrade purposes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;MySQL (&lt;code&gt;nextcloud.mysql&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;PHP (&lt;code&gt;nextcloud.php-fpm&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Apache (&lt;code&gt;nextcloud.apache&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Nextcloud Fixer (&lt;code&gt;nextcloud.nextcloud-fixer&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These run in order and are dependent on each other, so it&amp;rsquo;s informative to check them in order. Let&amp;rsquo;s talk through them.&lt;/p&gt;
&lt;h2 id=&#34;mysql&#34;&gt;MySQL&lt;/h2&gt;
&lt;p&gt;MySQL will run its own upgrade process when the snap update includes a new version of it. This alone can take surprisingly long (several minutes on my own production install of the snap).&lt;/p&gt;
&lt;p&gt;In most snap updates, though, a new MySQL version is not shipped. In that case, it should fire up pretty quickly. Either way, you can monitor it with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ snap logs -f nextcloud.mysql
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That will open the log and follow it, so you can see what&amp;rsquo;s happening in real time. To stop following it, ctrl+c out of it.&lt;/p&gt;
&lt;p&gt;If for whatever reason that log isn&amp;rsquo;t helpful to you, you can take a look at MySQL&amp;rsquo;s error log, located at &lt;code&gt;var/snap/nextcloud/current/logs/mysql_errors.log&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If MySQL looks healthy, move on to PHP.&lt;/p&gt;
&lt;h2 id=&#34;php&#34;&gt;PHP&lt;/h2&gt;
&lt;p&gt;PHP doesn&amp;rsquo;t really have an upgrade process of its own, even when the snap includes a PHP update. Which means it fires up fast and rarely has issues. That said, it&amp;rsquo;s required by almost everything else, so it&amp;rsquo;s important to know about it and know how to check it. You can monitor it with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ snap logs -f nextcloud.php-fpm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Its error log is located at &lt;code&gt;/var/snap/nextcloud/current/logs/php-fpm_errors.log&lt;/code&gt;. Assuming everything looks good, time to check Apache.&lt;/p&gt;
&lt;h2 id=&#34;apache&#34;&gt;Apache&lt;/h2&gt;
&lt;p&gt;Apache is another component that doesn&amp;rsquo;t have its own upgrade process and fires up fast. However, its the service that is responsible for actually running &lt;em&gt;Nextcloud&amp;rsquo;s&lt;/em&gt; upgrade process, so you&amp;rsquo;ll find that this is the most important service to monitor. You can do that with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ snap logs -f nextcloud.apache
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Following the pattern we&amp;rsquo;ve already established, its error log is located at &lt;code&gt;/var/snap/nextcloud/current/logs/apache_errors.log&lt;/code&gt;. If that looks good, then you should be able to visit Nextcloud at this time, but it might be in maintenance mode. That&amp;rsquo;s because it&amp;rsquo;s not the last step in the upgrade process&amp;ndash; check the Nextcloud Fixer.&lt;/p&gt;
&lt;h2 id=&#34;nextcloud-fixer&#34;&gt;Nextcloud Fixer&lt;/h2&gt;
&lt;p&gt;The Nextcloud Fixer is the slick and creative name that I gave the service that works around all of Nextcloud&amp;rsquo;s upgrade quirks and weirdnesses that we&amp;rsquo;ve run into over the years. For example, Nextcloud likes to introduce requirements in updates (new database columns, indices, primary keys) but then not take care of creating them for you. This is where we do such things. You can monitor that process with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ snap logs -f nextcloud.nextcloud-fixer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It doesn&amp;rsquo;t have a logfile of its own, so that command should give you everything.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If all of the above services look healthy and you still can&amp;rsquo;t visit the snap, something else is broken, probably not related to the snap. Check your surrounding infrastructure, your firewall, your port-forwarding etc., and make sure your IP address didn&amp;rsquo;t change out from under you so your domain name isn&amp;rsquo;t pointing to the wrong place.&lt;/p&gt;
&lt;p&gt;I hope that helps, but if you&amp;rsquo;re stumped and need some help, you&amp;rsquo;re always welcome to &lt;a href=&#34;https://github.com/nextcloud-snap/nextcloud-snap/issues&#34;&gt;log an issue&lt;/a&gt; and discuss it.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Nextcloud snap finds a new home</title>
      <link>https://kyrofa.com/posts/nextcloud-snap-finds-a-new-home/</link>
      <pubDate>Fri, 03 Sep 2021 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/nextcloud-snap-finds-a-new-home/</guid>
      <description>If you&amp;rsquo;re an avid reader of the blog (and come on, who isn&amp;rsquo;t?), you&amp;rsquo;ll know that I&amp;rsquo;ve been maintaining a snap for Nextcloud since Nextcloud came into existence. Originally, though, that snap wasn&amp;rsquo;t called &amp;ldquo;nextcloud&amp;rdquo;, it was called &amp;ldquo;nextcloud-kyrofa&amp;rdquo;. My original goal was that I would bootstrap the snap for them, and then they&amp;rsquo;d take it over when they were ready. I hosted the code as a personal project on GitHub.</description>
      <content:encoded>&lt;p&gt;If you&amp;rsquo;re an avid reader of the blog (and come on, who isn&amp;rsquo;t?), you&amp;rsquo;ll know that &lt;a href=&#34;https://kyrofa.com/posts/four-years-of-nextcloud&#34;&gt;I&amp;rsquo;ve been maintaining a snap for Nextcloud&lt;/a&gt; since Nextcloud came into existence. Originally, though, that snap wasn&amp;rsquo;t called &amp;ldquo;nextcloud&amp;rdquo;, it was called &amp;ldquo;nextcloud-kyrofa&amp;rdquo;. My original goal was that I would bootstrap the snap for them, and then they&amp;rsquo;d take it over when they were ready. I hosted the code as a personal project on GitHub. Once the Nextcloud Box looked like a certainty, though, &lt;a href=&#34;https://github.com/jospoortvliet&#34;&gt;Jos&lt;/a&gt; and I talked about transferring things over to Nextcloud&amp;ndash; both the code as well as the snap in the store, so we could call it &amp;ldquo;nextcloud&amp;rdquo; and rip my nick off the end.&lt;/p&gt;
&lt;p&gt;Ultimately they were on board, but they asked that I continue maintaining it, which was fine with me because I had since fallen in love with it and began using it myself. So I ended up maintaining the Nextcloud snap with the code living in the &lt;a href=&#34;https://github.com/nextcloud/nextcloud-snap&#34;&gt;Nextcloud GitHub org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That has worked fine for years, but I&amp;rsquo;m posting today to explain why it no longer works, and why the snap&amp;rsquo;s code is amicably moving away from the Nextcloud org to &lt;a href=&#34;https://github.com/nextcloud-snap/nextcloud-snap&#34;&gt;one that I fully control&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;what-happened&#34;&gt;What happened?&lt;/h2&gt;
&lt;p&gt;Nextcloud has gotten quite popular, and various pieces of it have attracted a number of contributors, which is wonderful. They are a very welcoming community, and I have found myself at odds with their open-by-default practices. I&amp;rsquo;ll admit I&amp;rsquo;m a bit of a grumpy old man, but allow me to explain.&lt;/p&gt;
&lt;p&gt;The Nextcloud org has a very low barrier to entry. Essentially, if you contribute anything at all to any of the projects in the org, you&amp;rsquo;re added to the org. It&amp;rsquo;s their org, and it&amp;rsquo;s cool to be able to show that badge on your GitHub profile etc., so I can see how such a process encourages the growth of the community. As of this writing there are &lt;a href=&#34;https://github.com/orgs/nextcloud/people&#34;&gt;619 people in that org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the problem, though: &lt;strong&gt;the base role for the Nextcloud org is write access&lt;/strong&gt;. That means that over 600 people have write access to any of the over-200 repositories in the Nextcloud org. Various Nextcloud apps, utilities, and yes, the snap. Over 600 people that I don&amp;rsquo;t know have write access to my project. Even as an admin of the project, GitHub seemingly provides no way for me to override the org&amp;rsquo;s base role. In my humble opinion, while I understand the desire to have an open community, this is poor opsec, and it makes me uncomfortable.&lt;/p&gt;
&lt;h2 id=&#34;so-protect-your-branches&#34;&gt;So protect your branches!&lt;/h2&gt;
&lt;p&gt;Those familiar with GitHub will suggest that I protect my branches. I do that on my &lt;em&gt;personal&lt;/em&gt; projects, you can bet I do that were I have collaborators (like here). Sadly, that&amp;rsquo;s really the only level of protection that GitHub offers. My CI system leans heavily on tags, and anyone in the org can delete them or make them point to another commit. Write access provides &lt;a href=&#34;(https://docs.github.com/en/organizations/managing-access-to-your-organizations-repositories/repository-permission-levels-for-an-organization)&#34;&gt;quite a bit of access&lt;/a&gt;. Even protected branches provide only a single layer of protection. I&amp;rsquo;m human, after all. If I forget to protect a release branch, it&amp;rsquo;s game over. Not everyone signs their commits (by the way, you should sign your commits).&lt;/p&gt;
&lt;p&gt;And this goes beyond the code, too. There have been numerous times where some random community member will run rampant over my issues without any communication, creating labels and applying them willy nilly. I hate to be ungrateful, but I have a system for things like that. Instead of being helpful, it creates more work for me.&lt;/p&gt;
&lt;h2 id=&#34;web-of-trust&#34;&gt;Web of trust&lt;/h2&gt;
&lt;p&gt;My trust is earned, not granted by default. I like to invite individuals that have earned that trust to join the team that maintains a particular project. It leads to a much smaller community than Nextcloud&amp;rsquo;s approach, but that community is built on trust as opposed to random contributions to a completely different project. I don&amp;rsquo;t mean to imply that one approach is objectively better than the other&amp;ndash; they serve different purposes.&lt;/p&gt;
&lt;p&gt;Snaps automatically update. As a result, the people installing the Nextcloud snap are willingly ceding control of and responsibility for maintaining and updating their Nextcloud stack to the snap maintainers. They are putting their &lt;strong&gt;trust&lt;/strong&gt; in us&amp;ndash; in me&amp;ndash; and I take that very seriously. If I then allow people I &lt;em&gt;don&amp;rsquo;t&lt;/em&gt; trust to have write access to the snap, do I really deserve that trust? I think not. So I&amp;rsquo;m taking action.&lt;/p&gt;
&lt;h2 id=&#34;whats-the-plan&#34;&gt;What&amp;rsquo;s the plan?&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s simple: with Nextcloud&amp;rsquo;s blessing, the Nextcloud snap &lt;a href=&#34;https://github.com/nextcloud-snap&#34;&gt;has moved to the nextcloud-snap org on GitHub&lt;/a&gt;, which I own. Now I can promise that the only people with access are the people who have earned my trust. Nothing should change in the day-to-day maintenance of the snap, and GitHub is forwarding traffic from the old URL for those of you who have it cloned (just update your remotes when you get a chance).&lt;/p&gt;
&lt;p&gt;As always, I appreciate your support and your trust, and I will do everything in my power to retain it. I look forward to continuing to work with you from our new location!&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Demystifying Rails autoloading</title>
      <link>https://kyrofa.com/posts/demystifying-rails-autoloading/</link>
      <pubDate>Sat, 14 Aug 2021 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/demystifying-rails-autoloading/</guid>
      <description>When I first started learning Rails back in the day, it was my first introduction to Ruby: I was learning them both at the same time. As a result, the line between them was rather blurry; I didn&amp;rsquo;t know what was coming from Ruby, and what was coming from Rails. The Rails approach of monkey-patching Ruby didn&amp;rsquo;t help. If I&amp;rsquo;m being honest, I didn&amp;rsquo;t realize that Object#blank? wasn&amp;rsquo;t a Ruby method until only a few years ago.</description>
      <content:encoded>&lt;p&gt;When I first started learning Rails back in the day, it was my first introduction to Ruby: I was learning them both at the same time. As a result, the line between them was rather blurry; I didn&amp;rsquo;t know what was coming from Ruby, and what was coming from Rails. The Rails approach of monkey-patching Ruby didn&amp;rsquo;t help. If I&amp;rsquo;m being honest, I didn&amp;rsquo;t realize that &lt;code&gt;Object#blank?&lt;/code&gt; wasn&amp;rsquo;t a Ruby method until only a few years ago.&lt;/p&gt;
&lt;p&gt;I think it&amp;rsquo;s really important to understand the software you&amp;rsquo;re using, particularly big frameworks like Rails that follow convention over configuration and thus end up magically doing things for you. What are you supposed to do when the magic is gone?&lt;/p&gt;
&lt;p&gt;Today I want to talk about one such piece of magic: why you need to &lt;code&gt;require&lt;/code&gt; things in Ruby, and why you (generally) don&amp;rsquo;t in Rails.&lt;/p&gt;
&lt;p&gt;First of all, you&amp;rsquo;ll note that the title refers to Rails autoloading. I did that for the SEO, but that&amp;rsquo;s not the only piece in play, here. In fact, there are three:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Ruby&lt;/li&gt;
&lt;li&gt;Bundler&lt;/li&gt;
&lt;li&gt;Rails&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&amp;rsquo;s take each in turn. Note that for all my examples, I&amp;rsquo;m using version 3.0.1 of Ruby via RVM, with a gemset made particularly for this purpose.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ rvm install 3.0.1
$ rvm use 3.0.1@demystify-autoloading --create
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;ruby&#34;&gt;Ruby&lt;/h2&gt;
&lt;p&gt;Ruby does no hand-holding, and has no magic. In general, you must require everything. Let&amp;rsquo;s investigate this with a simple example.&lt;/p&gt;
&lt;p&gt;Create a new directory for this example with two files in it, one of them executable:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ mkdir ~/ruby-example
$ touch ~/ruby-example/foo.rb
$ touch ~/ruby-example/main.rb
$ chmod a+x ~/ruby-example/main.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make &lt;code&gt;~/ruby-example/foo.rb&lt;/code&gt; look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Foo&lt;/span&gt;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;hello&lt;/span&gt;
    puts &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;hello, world!&amp;#39;&lt;/span&gt;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This file defines a simple class with a single method that prints a message. Now let&amp;rsquo;s fill out &lt;code&gt;~/ruby-example/main.rb&lt;/code&gt; to use it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/env ruby&lt;/span&gt;

foo &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;Foo&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;new
foo&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;hello
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This simply instantiates our class and runs the method that should print our message. Let&amp;rsquo;s run it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ~/ruby-example/main.rb
/home/kyrofa/ruby-example/main.rb:3:in `&amp;lt;main&amp;gt;&#39;: uninitialized constant Foo (NameError)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This doesn&amp;rsquo;t work because Ruby has no magic here: it has no idea what Foo is, despite the fact that the file defining it is sitting right next to the one you&amp;rsquo;re executing. You need to require it. Make &lt;code&gt;~/ruby-example/main.rb&lt;/code&gt; look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/env ruby&lt;/span&gt;

require_relative &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;

foo &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;Foo&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;new
foo&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;hello
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Run it again, and behold success:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ~/ruby-example/main.rb
hello, world!
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In general you need to be pretty explicit in Ruby about your dependencies. In trade, it&amp;rsquo;s usually pretty easy to determine where your dependencies are coming from, since there&amp;rsquo;s an explicit require chain that pulls it in.&lt;/p&gt;
&lt;h2 id=&#34;bundler&#34;&gt;Bundler&lt;/h2&gt;
&lt;p&gt;One could make the case that needing to explicitly include everything you&amp;rsquo;re using is tedius. This is actually one of the roles that Bundler can play. Most folks know that Bundler can be used to manage your dependencies, but did you know that it can also be used to &lt;code&gt;require&lt;/code&gt; those dependencies?&lt;/p&gt;
&lt;p&gt;Create a new directory for this example with two files in it, one of them executable:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ mkdir ~/bundler-example
$ touch ~/bundler-example/Gemfile
$ touch ~/bundler-example/main.rb
$ chmod a+x ~/bundler-example/main.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;~/bundler-example/Gemfile&lt;/code&gt; is used by bundler to control your dependencies. Make it look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;source &#39;https://rubygems.org&#39;
gem &#39;hello-world&#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;hello-world&lt;/code&gt; gem is useful because it just prints a message when you &lt;code&gt;require&lt;/code&gt; it. Install it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cd ~/bundler-example/
$ bundle install
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now make &lt;code&gt;~/bundler-example/main.rb&lt;/code&gt; look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/env ruby&lt;/span&gt;

require &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;bundler/setup&amp;#39;&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;Bundler&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;require
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. Run it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ~/bundler-example/main.rb
hello world!
this is hello world library
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That&amp;rsquo;s the message printed by the &lt;code&gt;hello-world&lt;/code&gt; gem when you &lt;code&gt;require&lt;/code&gt; it, as I mentioned above. That means Bundler included it for you, as a result of it being part of your Gemfile. You can hand &lt;code&gt;Bundler.require&lt;/code&gt; different groups to limit what it &lt;code&gt;requires&lt;/code&gt; for you, as well (&lt;code&gt;production&lt;/code&gt; versus &lt;code&gt;development&lt;/code&gt; or &lt;code&gt;test&lt;/code&gt;, for example).&lt;/p&gt;
&lt;p&gt;Now that you&amp;rsquo;re familiar with this technique, go to any Rails application and check out &lt;code&gt;config/application.rb&lt;/code&gt;, and you&amp;rsquo;ll see something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;

&lt;span style=&#34;color:#75715e&#34;&gt;# Require the gems listed in Gemfile, including any gems&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# you&amp;#39;ve limited to :test, :development, or :production.&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;Bundler&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;require(&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;Rails&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;groups)

&lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So Rails applications do this for you by default, which is why you generally don&amp;rsquo;t need to &lt;code&gt;require&lt;/code&gt; any of your dependencies. There are caveats, however.&lt;/p&gt;
&lt;p&gt;Lots of Rails developers (including me) have settled on a modular approach to developing Rails applications, which means that a given application is split across a number of gems, which are often Rails engines. If you&amp;rsquo;re also in this camp, one thing to keep in mind is that gems (including engines) do not have this functionality. Gems generally execute within the context of the main application&amp;rsquo;s Gemfile, so it can&amp;rsquo;t lean on Bundler like this. Gems are expected to &lt;code&gt;require&lt;/code&gt; their own dependencies. That doesn&amp;rsquo;t mean you need to do it in every file, though, if that annoys you. Ruby&amp;rsquo;s &lt;code&gt;require&lt;/code&gt; has global effect, which means once something is &lt;code&gt;require&lt;/code&gt;d in one place, it can be used everywhere. If you have a file which is generally loaded as part of using the gem (e.g. &lt;code&gt;lib/&amp;lt;gem name&amp;gt;.rb&lt;/code&gt;), you can put your &lt;code&gt;require&lt;/code&gt;s there and just use them everywhere.&lt;/p&gt;
&lt;h2 id=&#34;rails&#34;&gt;Rails&lt;/h2&gt;
&lt;p&gt;So Bundler magic covers why you don&amp;rsquo;t need to &lt;code&gt;require&lt;/code&gt; your gem dependencies, but what about other .rb files within the same app (or engine)? If you&amp;rsquo;re familiar with Rails, you may have noticed you generally don&amp;rsquo;t need to &lt;code&gt;require&lt;/code&gt; those either. That flies in the face of our understanding of Ruby, and it&amp;rsquo;s thanks to Rails magic called autoloading.&lt;/p&gt;
&lt;p&gt;There are &lt;a href=&#34;https://www.urbanautomaton.com/blog/2013/08/27/rails-autoloading-hell/&#34;&gt;other&lt;/a&gt; &lt;a href=&#34;https://guides.rubyonrails.org/autoloading_and_reloading_constants.html&#34;&gt;resources&lt;/a&gt; that describe Rails autoloading in detail. Those are recommended reading, so I won&amp;rsquo;t repeat them.&lt;/p&gt;
&lt;p&gt;The general idea is this: if you access a constant that is missing in the current execution context (e.g. a class defined in another file), Rails will try to find and &lt;code&gt;require&lt;/code&gt; the file that defines it for you by searching a pre-defined set of autoload paths following a convention where each namespace for the constant equals a directory. A simplistic example: if you try to access &lt;code&gt;Bar&lt;/code&gt; and it&amp;rsquo;s not defined, Rails will quickly check to see if there&amp;rsquo;s a &lt;code&gt;bar.rb&lt;/code&gt; directory in any of the autoload paths. If there&amp;rsquo;s not, it&amp;rsquo;ll either complain or you&amp;rsquo;ll get the standard Ruby error for trying to access an undefined thing. If you try to access &lt;code&gt;MyModule::Bar&lt;/code&gt;, it&amp;rsquo;ll search those same autoload paths for &lt;code&gt;my_module/bar.rb&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As a caveat, Rails engines don&amp;rsquo;t add their &lt;code&gt;lib/&lt;/code&gt; directory to the set of autoload paths by default, but you&amp;rsquo;ll notice engine developers often do it themselves for consistent behavior.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Rails has done a magnificent job of making it trivial to get up and running developing new web applications, thanks to its convention-over-configuration approach. Its use of Bundler and autoloading to handle &lt;code&gt;require&lt;/code&gt;s is no small part of that overall philosophy, and I suspect it&amp;rsquo;s a large reason for its success. That said, if you never really try to understand what Rails is doing for you, you will eventually get bitten when it doesn&amp;rsquo;t do what you expect. Gaining at least a small insight into how this particular piece of the puzzle works will pay dividends down the road, and I hope this was helpful in accomplishing exactly that.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>My phone isn&#39;t as smart as yours</title>
      <link>https://kyrofa.com/posts/my-phone-isn-t-as-smart-as-yours/</link>
      <pubDate>Sat, 17 Apr 2021 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/my-phone-isn-t-as-smart-as-yours/</guid>
      <description>I remember the first mobile phone I ever saw. A &amp;ldquo;car phone.&amp;rdquo; It looked a bit like an old corded telephone hooked up to a car battery in a bag that weighed 30 pounds. I thought it was awesome; my father was on the bleeding edge of a technological revolution, and he was pretty much a super hero. Was it pretty? No. Actually mobile? It couldn&amp;rsquo;t leave the car, but the car could move, so sure.</description>
      <content:encoded>&lt;p&gt;I remember the first mobile phone I ever saw. A &amp;ldquo;car phone.&amp;rdquo; It looked a bit like an old corded telephone hooked up to a car battery in a bag that weighed 30 pounds. I thought it was awesome; my father was on the bleeding edge of a technological revolution, and he was pretty much a super hero. Was it pretty? No. Actually mobile? It couldn&amp;rsquo;t leave the car, but the car could move, so sure. Did it give us all cancer? Maybe.&lt;/p&gt;
&lt;h2 id=&#34;in-the-beginning&#34;&gt;In the beginning&lt;/h2&gt;
&lt;p&gt;I received my first personal cell phone when I was 16. A Nokia something-or-other. It wasn&amp;rsquo;t small enough to fit in my pocket, but it came with a case and a belt clip, so I looked like a boss. Texting was just starting to become a thing. I couldn&amp;rsquo;t understand it&amp;ndash; why on earth would I punish myself trying to produce something legible by randomly mashing buttons (a.k.a. the super mario method, a.k.a. T9)? I&amp;rsquo;d rather call people instead. And you know what? Back then, people answered instead of ignoring you and texting you back, probably because they agreed texting was annoying, too. If they didn&amp;rsquo;t answer, you left a voicemail, which was a perfectly normal thing to do.&lt;/p&gt;
&lt;p&gt;By the time I left for university, my cell phone was a Sony Ericsson Walkman flip phone. It was small enough to fit in my pocket, but I still wore it on a belt clip, because I was cool. That phone could actually play music, so I was also regularly wearing headphones, which really just elevated my cool factor.&lt;/p&gt;
&lt;p&gt;During this journey, I wasn&amp;rsquo;t paying for my phone or the plan, my parents (bless their hearts) were. I never wanted to rock that boat: when it was time to get a new phone, they gave me my options, and I happily selected one. I knew that smartphones were a thing. Heck, I was a Computer Engineering major&amp;ndash; I knew all about them. But they were expensive, and I never dared ask my parents. Then came graduation day, and First Real Job day, when I no longer mooched from my parents.&lt;/p&gt;
&lt;h2 id=&#34;smartphones-forever&#34;&gt;Smartphones forever!&lt;/h2&gt;
&lt;p&gt;I moved across the country for work, and purchased my own phone and my own phone plan. A smartphone, obviously: the HTC One. It was perfect. The perfect size, the perfect speed. Battery life was a little annoying but that was okay, and I could actually text on the thing!&lt;/p&gt;
&lt;p&gt;It changed my life. I didn&amp;rsquo;t need maps anymore to get around. I started texting far more than I called. I received my email on my phone, allowing me to be more responsive and use my computer less. More than anything: &lt;strong&gt;it put the internet at my fingertips at all times&lt;/strong&gt;. I could learn anything at the drop of a hat. I was hooked. I purchased a new smartphone every two years, upgrading to the best thing on the market at the time, unlimited data plan, the works.&lt;/p&gt;
&lt;p&gt;To people who know me, I&amp;rsquo;m sure this is unsurprising. I&amp;rsquo;m a pretty nerdy dude. Come on, I wore cell phones on my &lt;em&gt;hip&lt;/em&gt;. Why &lt;em&gt;wouldn&amp;rsquo;t&lt;/em&gt; I stay on the forefront of innovation?&lt;/p&gt;
&lt;p&gt;So it might surprise you to know that I&amp;rsquo;m approaching the third anniversary of owning my latest phone, an Alcatel GO FLIP. Yes, a flip phone. It doesn&amp;rsquo;t even play music. That surprises people, and I&amp;rsquo;ve gotten a lot of questions about it. What happened to me? That&amp;rsquo;s what I want to tell you about today.&lt;/p&gt;
&lt;h2 id=&#34;all-was-not-right-with-the-world&#34;&gt;All was not right with the world&lt;/h2&gt;
&lt;p&gt;It really comes down to something I&amp;rsquo;m loathe to admit: I have no self-discipline. For example, the only reason I don&amp;rsquo;t weigh 300 pounds is because my lovely wife knows that if she buys chips/cookies/what-have-you, I will eat them all without prejudice. So such things stay out of the house. I don&amp;rsquo;t seem to have a problem with binge eating broccoli.&lt;/p&gt;
&lt;p&gt;This lack of self-discipline shows itself in various ways. One was addiction. No, thankfully I&amp;rsquo;ve never struggled with alcohol or drug addiction. For me, it was &lt;em&gt;information&lt;/em&gt;. I love learning new things so much that it can easily become &amp;ldquo;the end&amp;rdquo; instead of &amp;ldquo;the means&amp;rdquo;. This happens to everyone in some form or another: you want to make sure you can explain gimbal lock properly, so you go to the wikipedia page, where you see Apollo 11 discussed, so you check that out, where you see talk of the ATOLL programming language, so you start reading about that before you realize that you should really finish the gimbal lock thing. It got to the point where having a smartphone with me all the time was virtually &lt;em&gt;that&lt;/em&gt;, constantly. I wasn&amp;rsquo;t continually playing games or using snapchat like the kids of today, but I was no better. In fact, I was worse. I was always sucked into learning the ins and outs of whatever tickled my fancy at that moment, which required far more focus and withdrawal from the real world than snapchat ever could.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s not to say I couldn&amp;rsquo;t focus. I could, and I did. In fact, I believe that this propensity is properly harnessed when writing software, and thus is a significant part of why I like to think I&amp;rsquo;m good at my job. I am obsessed with writing the best code I can, which involves constant research and learning new ways to do things. As a result, I don&amp;rsquo;t really have the same struggles when on a computer working than I do with a smartphone. No, it was my free time that slowly disintegrated.&lt;/p&gt;
&lt;p&gt;When I got off work, I&amp;rsquo;d go into the living room where the kids were playing. I adore my sweet kids; I&amp;rsquo;d give them all hugs and enjoy the excited &amp;ldquo;daddy&amp;rsquo;s home!&amp;rdquo; reactions, then I&amp;rsquo;d plop down into the recliner and pull out my phone. The kids eventually gave up on trying to get my attention, and I only had reason to look away from my screen when they were misbehaving and needed correcting. Which meant two things: 1) they misbehaved more to get my attention, and 2) our interactions were primarily limited to my disciplining them. Neither of those were great.&lt;/p&gt;
&lt;p&gt;I hadn&amp;rsquo;t read a book for years. My free time was spent on my phone learning something or other, but learning it in a very specific, very shallow way.&lt;/p&gt;
&lt;p&gt;My work at Canonical over the last few years had gotten more stressful. I had to keep a lot more in my head. At the same time, it seemed like my attention span was getting shorter and shorter. My brain felt like it was fracturing, and I started getting irritable. And I still plopped in that recliner at the end of every day and pumped more useless information into my overworked head.&lt;/p&gt;
&lt;p&gt;Suddenly, my wife made an interesting admission: &amp;ldquo;I think I&amp;rsquo;m addicted to Facebook. I feel like I&amp;rsquo;m on my phone too much when I&amp;rsquo;m around the kids, and I don&amp;rsquo;t see the quiet positive things that happen as much as the loud misbehaving. And I think that is negatively impacting our relationship.&amp;rdquo; Suddenly everything clicked into place.&lt;/p&gt;
&lt;h2 id=&#34;i-know-lets-rely-on-our-self-discipline&#34;&gt;I know! Let&amp;rsquo;s rely on our self-discipline!&lt;/h2&gt;
&lt;p&gt;We started a policy of leaving our phones on the shelf after work. That was nice&amp;ndash; the kids behaved better, our relationship improved, and my brain started to heal. As you might expect, though, the lack of self-discipline bit us over and over again. We would find after a few days that we fell back into old habits, and would need to re-institute the policy, only to fall back into old habits again.&lt;/p&gt;
&lt;h2 id=&#34;the-return-of-t9&#34;&gt;The return of T9&lt;/h2&gt;
&lt;p&gt;Finally, we took the broccoli approach: we ordered flip phones. Used from ebay, because, you know, they don&amp;rsquo;t really exist anymore. We were thinking that maybe we just needed a detox. Maybe if we could actually be without smartphones for a while, we&amp;rsquo;d be able to go back and have more discipline with them.&lt;/p&gt;
&lt;p&gt;The transition wasn&amp;rsquo;t easy, though. There have been a number of issues that we had to deal with that really ended up making this a complete life change.&lt;/p&gt;
&lt;h3 id=&#34;no-more-camera-in-your-pocket&#34;&gt;No more camera in your pocket&lt;/h3&gt;
&lt;p&gt;My wife&amp;rsquo;s main gripe was the lack of a decent camera. Flip phones generally have cameras, but she liked the ability to snap the kids doing something memorable and save it forever, and no one wants to save the kind of picture that is taken by a flip phone camera. Also, she&amp;rsquo;s a &lt;a href=&#34;https://monicafazzari.com/&#34;&gt;photographer&lt;/a&gt;, so her standards are even higher than most. I actually ended up purchasing her a high-quality compact camera for her birthday. That fits in her purse, and shoots in RAW just like her real-deal cameras. She likes that, and has mostly stopped whining about this aspect of flip phones.&lt;/p&gt;
&lt;h3 id=&#34;no-more-instant-calendar&#34;&gt;No more instant calendar&lt;/h3&gt;
&lt;p&gt;Something that has effected us both is the lack of a calendar system. I&amp;rsquo;ve had a &lt;a href=&#34;https://nextcloud.com/&#34;&gt;Nextcloud&lt;/a&gt; instance for years that we used to sync our contacts and calendars on our phones. It was really nice to be able to organize things that way. My wife now maintains an actual calendar, a spiral bound book on our counter. I know that sounds annoying, but it has actually worked great. The only real downside is that you can only &amp;ldquo;check your calendar&amp;rdquo; when you&amp;rsquo;re home.&lt;/p&gt;
&lt;h3 id=&#34;group-messaging-is-hit-or-miss&#34;&gt;Group messaging is hit or miss&lt;/h3&gt;
&lt;p&gt;This one was a big deal. I didn&amp;rsquo;t realize 1) how often I texted all of my siblings as a group, and 2) that group messaging support in flip phones is hit or miss. It&amp;rsquo;s impossible to determine if a given flip phone supports them without trying it. My first two flip phones were misses: a Nokia and a Kyocera. I thus went years without being able to be included in a group text thread at all. I was seriously considering creating my own, to be honest. Thankfully, I think I figured it out: if the phone is based on &lt;a href=&#34;https://source.android.com/&#34;&gt;AOSP&lt;/a&gt; or &lt;a href=&#34;https://www.kaiostech.com/&#34;&gt;KaiOS&lt;/a&gt;, group messaging seems to be supported. The Alcatel GO FLIP uses the latter, and group messaging works great.&lt;/p&gt;
&lt;h3 id=&#34;seriously-t9&#34;&gt;Seriously, T9?&lt;/h3&gt;
&lt;p&gt;Thankfully, not completely T9. Even dumbphone users aren&amp;rsquo;t that masochistic. The OS is smart enough to have a dictionary and predictive text, so it can mostly figure out what you&amp;rsquo;re trying to do. It&amp;rsquo;s still tremendously annoying though, I won&amp;rsquo;t lie. And we live in a smartphone world: people expect you to text back. You should see my wife, she could probably break some records with how fast she can crank out a text on that teeny keyboard. Me&amp;hellip; not so much. My texts are very plain and to-the-point. And you know what&amp;hellip; maybe that&amp;rsquo;s just the kind of person I should be.&lt;/p&gt;
&lt;h3 id=&#34;maps-and-gps&#34;&gt;Maps and GPS&lt;/h3&gt;
&lt;p&gt;I grew up in a small town, and to this day I still don&amp;rsquo;t know any street names; I drive by landmark. I have an extraordinary inability to navigate anywhere. It&amp;rsquo;s just not one of my skills, and that&amp;rsquo;s an endless source of mirth for my wife. Thankfully, it&amp;rsquo;s one of &lt;em&gt;her&lt;/em&gt; skills.&lt;/p&gt;
&lt;p&gt;When I moved to the big city, though, I wasn&amp;rsquo;t married yet. Virtually the only way I could get anywhere was to either buy a map, or use the GPS on the shiny new smartphone I had. You can probably guess the direction I went with that. I became entirely dependent on that thing.&lt;/p&gt;
&lt;p&gt;Flip phones, barring a few outliers, don&amp;rsquo;t have maps. This has been a difficult adjustment for me. When I&amp;rsquo;m going somewhere I&amp;rsquo;ve never been, I generally will just print out Google Maps directions. If it&amp;rsquo;s somewhere a long ways away, though, where I might need to take numerous detours, I actually just bring my old smartphone with an offline map. That&amp;rsquo;s what I do whenever I travel internationally as well (where I don&amp;rsquo;t have signal), and it works perfectly.&lt;/p&gt;
&lt;h3 id=&#34;alternative-sources-of-music&#34;&gt;Alternative sources of music&lt;/h3&gt;
&lt;p&gt;My family is one of those that listens to music virtually constantly. At home, in the car, on an airplane, everywhere. I always purchased smartphones with a micro SD card slot so I could fit my 40GB or so of music on there. My father got us a Spotify subscription for Christmas one year, and we started using that as well. Flip phones generally do neither of these things.&lt;/p&gt;
&lt;p&gt;When we&amp;rsquo;re in the car, we&amp;rsquo;re now listening to CDs or the radio (how retro!). For longer trips, where I generally have the old smartphone for maps anyway, we use it for music just like the old (new?) days.&lt;/p&gt;
&lt;h3 id=&#34;severe-lack-of-knowing&#34;&gt;Severe lack of knowing&lt;/h3&gt;
&lt;p&gt;One of the beauties of having a computer in your pocket at all times is having instant access to all of mankind&amp;rsquo;s knowledge whenever you need it. Need a recipe for garlic knots? Done. Can&amp;rsquo;t remember that joke you read? Look it up real quick so you can tell it properly. Need to explain gimbal lock? As I said earlier, I took full advantage of this with a smartphone, but ultimately &lt;em&gt;it&amp;rsquo;s what drove me to a dumbphone&lt;/em&gt;. I&amp;rsquo;m just not made to have constant access to information like that. I&amp;rsquo;ve developed a new skill: &lt;strong&gt;the ability to say &amp;ldquo;I don&amp;rsquo;t know&amp;rdquo; and be okay with it&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s not to say I&amp;rsquo;ve stopped learning and researching things. It&amp;rsquo;s just far more&amp;hellip; natural. Instead of isolating myself in the moment, switching contexts, and instantly satisfying my curiosity, I make a mental note. If I remember later, I&amp;rsquo;ll research it on the computer when I have some free time. If I fail to remember, well, then it wasn&amp;rsquo;t really worth knowing, was it? I think that&amp;rsquo;s really how I&amp;rsquo;m made to live. &lt;strong&gt;The ability to forget is a gift, and I have it for a reason.&lt;/strong&gt; Shoving useless information into my face was how I got here in the first place. Ultimately, I&amp;rsquo;ve found the lack of knowing to be freedom.&lt;/p&gt;
&lt;h2 id=&#34;not-just-a-detox&#34;&gt;Not just a detox&lt;/h2&gt;
&lt;p&gt;Like I said earlier, we went into this thinking that we just needed a detox. It&amp;rsquo;s been three years though, I&amp;rsquo;m not really sure we can call it that anymore. Our lives have completely changed. It&amp;rsquo;s been a long road to my brain healing, but I&amp;rsquo;m stoked with the progress I&amp;rsquo;ve made. My attention span is fantastic now. I&amp;rsquo;ve generally found that I have a lot more free time, and I spend it on a few specific things instead of dabbling lightly in several without actually doing anything. I have some open-source software that I maintain, and I&amp;rsquo;ve gotten into woodworking and reading. I can&amp;rsquo;t even tell you how many books I&amp;rsquo;ve read.&lt;/p&gt;
&lt;p&gt;This caters to other desires I&amp;rsquo;ve always had, too. I don&amp;rsquo;t like being sold, and I don&amp;rsquo;t like being tracked, and smartphone companies make money by doing both. &amp;ldquo;If you&amp;rsquo;re not doing anything wrong, you have nothing to fear&amp;rdquo; says the naive person who has &lt;a href=&#34;https://www.nbcnews.com/news/us-news/google-tracked-his-bike-ride-past-burglarized-home-made-him-n1151761&#34;&gt;never really considered all the ramifications&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our relationships with our phones have changed completely. Half of the time I forget to bring mine when I leave the house. I&amp;rsquo;ll go an entire day without it before I realize it. I no longer fill silence with endless research: I have some empty space in my life. We have more meaningful discussions without any Facebook or Twitter notification sounds going off.&lt;/p&gt;
&lt;p&gt;It definitely impacted our relationship with our kids, too. When they look up while playing, we&amp;rsquo;re actually there, we notice and give them a smile. We notice how well they&amp;rsquo;re playing together and are able to give them some positive affirmation instead of over-emphasizing the negative. We see and appreciate the sweet moments instead of only really getting jerked out of our bubble by misbehavior.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ve even noticed a social impact. For example, it can be awkward to sit in a doctor&amp;rsquo;s office where everyone has their nose in their phones except for you. However, we&amp;rsquo;ve noticed that people tend to randomly strike up conversations with us, and not with other people in the room. We attribute this to the fact that we&amp;rsquo;re the only people that don&amp;rsquo;t appear preoccupied. We&amp;rsquo;re suddenly more approachable, and this has made it easier to connect with people.&lt;/p&gt;
&lt;p&gt;Ultimately, we&amp;rsquo;ve found that the upsides of this lifestyle outweigh the downsides. I suspect we&amp;rsquo;ll be &amp;ldquo;detoxing&amp;rdquo; for the forseeable future.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>The end of my journey at Canonical, and new adventures ahead</title>
      <link>https://kyrofa.com/posts/the-end-of-my-journey-at-canonical-and-new-adventures-ahead/</link>
      <pubDate>Wed, 10 Mar 2021 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/the-end-of-my-journey-at-canonical-and-new-adventures-ahead/</guid>
      <description>I am a staff engineer at Canonical, where I have worked for six years. My time here has been filled with brilliant people and amazing technology, and I&amp;rsquo;ve had the pleasure and privilege of playing pivotal roles getting a number of new projects off the ground. As I&amp;rsquo;ve said in the past, I joined Canonical to help my technical growth, and they delivered in spades. I have really loved my time here, which made the decision to leave all the more difficult.</description>
      <content:encoded>&lt;p&gt;I am a staff engineer at Canonical, where I have worked for six years. My time here has been filled with brilliant people and amazing technology, and I&amp;rsquo;ve had the pleasure and privilege of playing pivotal roles getting a number of new projects off the ground. As I&amp;rsquo;ve &lt;a href=&#34;https://kyrofa.com/posts/unity-8-s-snappy-scope&#34;&gt;said in the past&lt;/a&gt;, I joined Canonical to help my technical growth, and they delivered in spades. I have really loved my time here, which made the decision to leave all the more difficult. However, here I am, with the clock ticking down on my last week as a Canonical engineer.&lt;/p&gt;
&lt;h2 id=&#34;my-journey&#34;&gt;My journey&lt;/h2&gt;
&lt;p&gt;I was originally hired onto the Unity API team, helping create Unity 8 for mobile and desktop (you&amp;rsquo;re probably used to Unity 7). That team was filled with wonderful people that I really looked up to, from the kindest and most supportive manager I&amp;rsquo;d ever had, &lt;a href=&#34;https://github.com/alecu&#34;&gt;Alejandro&lt;/a&gt;, to C++ geniuses like &lt;a href=&#34;https://github.com/michihenning&#34;&gt;Michi&lt;/a&gt; and &lt;a href=&#34;https://github.com/pete-woods&#34;&gt;Pete&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Over time I found myself drawn more and more to this whole &amp;ldquo;snaps&amp;rdquo; thing, and eventually I was invited to join the team creating the &lt;a href=&#34;https://snapcraft.io/&#34;&gt;snapcraft&lt;/a&gt; CLI. This is where I met all-stars like &lt;a href=&#34;https://github.com/sergiusens&#34;&gt;Sergio&lt;/a&gt; and &lt;a href=&#34;https://github.com/elopio&#34;&gt;Leo&lt;/a&gt;. After a while Mark asked me to join the snapd team to help out with some specific features, which is where I got to know the amazing &lt;a href=&#34;https://github.com/mvo5&#34;&gt;Michael&lt;/a&gt;. Once those features were complete, I found myself jumping between both snapcraft and snapd to implement larger features, and the snapcraft team ended up merging with the advocacy team, where I had the privilege of working with giants like &lt;a href=&#34;https://github.com/popey&#34;&gt;Alan&lt;/a&gt; and &lt;a href=&#34;https://github.com/flexiondotorg&#34;&gt;Martin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Finally my robotics background came home to roost, and I was asked to bootstrap a robotics team at Canonical. I said something along the lines of &amp;ldquo;sure, as long as I don&amp;rsquo;t have to be a manager&amp;rdquo; and launched off. I had the absolute pleasure of being mentored by &lt;a href=&#34;https://github.com/joemcmanus&#34;&gt;Joe&lt;/a&gt; and &lt;a href=&#34;https://github.com/jdstrand&#34;&gt;Jamie&lt;/a&gt;, and I&amp;rsquo;m tremendously proud of what we&amp;rsquo;ve done and built in the robotics team at Canonical over the last few years!&lt;/p&gt;
&lt;p&gt;Now that the robotics team is up and running though, I&amp;rsquo;m ready for a new challenge and a change of pace.&lt;/p&gt;
&lt;h2 id=&#34;does-that-mean-youll-stop-maintaining-project&#34;&gt;Does that mean you&amp;rsquo;ll stop maintaining &amp;lt;project&amp;gt;?&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s true, you will probably see my activity on Canonical-owned projects decrease (e.g. snapcraft, snapd, and the like). However, everything that I did on my own time, I generally did because I was using it and relied on it (e.g. the nextcloud snap). Nothing should change for those types of projects.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m still a part of the Ubuntu and broader open source communities, so I hope our paths will continue to cross. You should feel free to reach out like you always have.&lt;/p&gt;
&lt;h2 id=&#34;so-what-is-next&#34;&gt;So what is next?&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m joining &lt;a href=&#34;https://www.miriamtech.com/&#34;&gt;Miriam Technologies&lt;/a&gt;, a software contracting and consulting company. I interned for Miriam in the past. It was my first software job as a kid, actually, and I&amp;rsquo;m looking forward to returning to where it all began and hopefully add a bit more value this time around. Every engineer there is friendly and brilliant, so if you ever need some custom software work done, get in touch.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>How to build a snap using ROS 2 Foxy</title>
      <link>https://kyrofa.com/posts/how-to-build-a-snap-using-ros-2-foxy/</link>
      <pubDate>Mon, 17 Aug 2020 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/how-to-build-a-snap-using-ros-2-foxy/</guid>
      <description>The snapcraft CLI (the tool used to create snaps) has long had support for building snaps that use both ROS 1 and ROS 2. ROS 2 Foxy Fitzroy is the latest ROS 2 LTS, which runs on Ubuntu 20.04 (Focal Fossa). The snapcraft CLI recently gained experimental support for building Foxy snaps, so I wanted to walk you through doing exactly that with the goal of helping both of us: getting you familiar with this new feature, and at the same time getting some mileage on this so as to make it not experimental.</description>
      <content:encoded>&lt;p&gt;The snapcraft CLI (the tool used to create &lt;a href=&#34;https://snapcraft.io/&#34;&gt;snaps&lt;/a&gt;) has long had support for building snaps that use both ROS 1 and ROS 2. ROS 2 Foxy Fitzroy is the latest ROS 2 LTS, which runs on Ubuntu 20.04 (Focal Fossa). The snapcraft CLI recently gained experimental support for building Foxy snaps, so I wanted to walk you through doing exactly that with the goal of helping both of us: getting you familiar with this new feature, and at the same time getting some mileage on this so as to make it &lt;em&gt;not&lt;/em&gt; experimental. Let&amp;rsquo;s get to it!&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;In order to follow along, you&amp;rsquo;ll need at least v4.2 of the snapcraft CLI:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install --classic snapcraft
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Of course, some previous experience &lt;a href=&#34;https://snapcraft.io/&#34;&gt;building a snap&lt;/a&gt; will also be helpful.&lt;/p&gt;
&lt;h2 id=&#34;lets-get-started&#34;&gt;Let&amp;rsquo;s get started&lt;/h2&gt;
&lt;p&gt;The first step toward creating any snap is to create the &lt;code&gt;snapcraft.yaml&lt;/code&gt;, which serves as the recipe instructing the snapcraft CLI on how to piece together your snap.&lt;/p&gt;
&lt;h3 id=&#34;create-the-snapcraftyaml&#34;&gt;Create the snapcraft.yaml&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s create a new directory, and then initialize it with a &lt;code&gt;snapcraft.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ mkdir ~/ros2-foxy-snap
$ cd ~/ros2-foxy-snap
$ snapcraft init
Created snap/snapcraft.yaml.
Go to https://docs.snapcraft.io/the-snapcraft-format/8337 &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; more information about the snapcraft.yaml format.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Open that &lt;code&gt;snap/snapcraft.yaml&lt;/code&gt; file, and make it look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ros2-talker-listener&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0.1&amp;#39;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;summary&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ROS2 Talker/Listener Example&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;description&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &lt;/span&gt;  &lt;span style=&#34;color:#ae81ff&#34;&gt;This example launches a ROS2 talker and listener.&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;grade&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;devel&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;confinement&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;strict&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;base&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;core20&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;parts&lt;/span&gt;:
  &lt;span style=&#34;color:#f92672&#34;&gt;ros-demos&lt;/span&gt;:
    &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;colcon&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;source&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;https://github.com/ros2/demos.git&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;source-branch&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;foxy&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;source-subdir&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;demo_nodes_cpp&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;build-packages&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;make, gcc, g++]&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;stage-packages&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;ros-foxy-ros2launch]&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;apps&lt;/span&gt;:
  &lt;span style=&#34;color:#f92672&#34;&gt;ros2-talker-listener&lt;/span&gt;:
    &lt;span style=&#34;color:#f92672&#34;&gt;command&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;opt/ros/foxy/bin/ros2 launch demo_nodes_cpp talker_listener.launch.py&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;plugs&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;network, network-bind]&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;extensions&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;ros2-foxy]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s break that down by section.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ros2-talker-listener&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0.1&amp;#39;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;summary&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ROS2 Talker/Listener Example&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;description&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &lt;/span&gt;  &lt;span style=&#34;color:#ae81ff&#34;&gt;This example launches a ROS2 talker and listener.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is the basic metadata required by all snaps. These fields are fairly self-explanatory, but note that the &lt;code&gt;name&lt;/code&gt; must be globally unique among all snaps. You might consider appending your developer name to the end of the snap name, for example.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;grade&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;devel&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;confinement&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;strict&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;base&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;core20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;grade&lt;/code&gt; can be either &lt;code&gt;stable&lt;/code&gt; or &lt;code&gt;devel&lt;/code&gt;. If it&amp;rsquo;s &lt;code&gt;devel&lt;/code&gt;, the store will prevent you from releasing into one of the two stable channels (&lt;code&gt;stable&lt;/code&gt; and &lt;code&gt;candidate&lt;/code&gt;, specifically). If it&amp;rsquo;s &lt;code&gt;stable&lt;/code&gt;, you can release it anywhere.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;confinement&lt;/code&gt; can be &lt;code&gt;strict&lt;/code&gt;, &lt;code&gt;devmode&lt;/code&gt;, or &lt;code&gt;classic&lt;/code&gt;. &lt;code&gt;strict&lt;/code&gt; enforces confinement, whereas &lt;code&gt;devmode&lt;/code&gt; allows all accesses, even those that would be disallowed under &lt;code&gt;strict&lt;/code&gt; confinement, and logs access that would be disallowed. &lt;code&gt;classic&lt;/code&gt; is even less confined than &lt;code&gt;devmode&lt;/code&gt; in that it doesn&amp;rsquo;t even get private namespaces anymore (among other things). There is &lt;a href=&#34;https://snapcraft.io/docs/snap-confinement&#34;&gt;more extensive documentation&lt;/a&gt; on confinement available.&lt;/p&gt;
&lt;p&gt;As I&amp;rsquo;ve said multiple times in the past, I typically use &lt;code&gt;strict&lt;/code&gt; confinement unless I know for sure that the thing I&amp;rsquo;m snapping won&amp;rsquo;t run successfully under confinement, in which case I&amp;rsquo;ll use &lt;code&gt;devmode&lt;/code&gt;. I typically avoid &lt;code&gt;classic&lt;/code&gt; unless I never intend for the snap to run confined (e.g. you&amp;rsquo;ll notice the snapcraft CLI is a classic snap, since it needs more access to the host than confinement would allow).&lt;/p&gt;
&lt;p&gt;Finally, the &lt;code&gt;base&lt;/code&gt; keyword specifies a special kind of snap that provides a minimal set of libraries common to most applications (e.g. libc). It will be the root filesystem for this snap. In this case, we&amp;rsquo;re using &lt;code&gt;core20&lt;/code&gt; which is a minimal rootfs based upon Ubuntu 20.04 (Focal).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;parts&lt;/span&gt;:
  &lt;span style=&#34;color:#f92672&#34;&gt;ros-demos&lt;/span&gt;:
    &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;colcon&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;source&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;https://github.com/ros2/demos.git&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;source-branch&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;foxy&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;source-subdir&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;demo_nodes_cpp&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;build-packages&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;make, gcc, g++]&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;stage-packages&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;ros-foxy-ros2launch]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The snapcraft CLI is responsible for taking many disparate parts and orchestrating them all into one cohesive snap. You tell it the parts that make up your snap, and it takes care of the rest. Here, we&amp;rsquo;re saying that we have a single part called &lt;code&gt;ros-demos&lt;/code&gt;. We specify that it builds using the colcon plugin, and we point it to the ROS 2 demos GitHub repository (this could just as easily be a directory on disk). We point the colcon plugin at the subdirectory containing the C++ demo nodes as opposed to letting it build the whole thing. We also provide a list of packages that need to be installed in order to build (&lt;code&gt;build-packages&lt;/code&gt;), and also ask that &lt;code&gt;ros-foxy-ros2launch&lt;/code&gt; gets staged into the snap alongside the rest of the part to be used at runtime (specifically, we&amp;rsquo;ll use it in the app, below). To view all the options supported by the &lt;code&gt;colcon&lt;/code&gt; plugin, run the command &lt;code&gt;snapcraft help colcon&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apps&lt;/span&gt;:
  &lt;span style=&#34;color:#f92672&#34;&gt;ros2-talker-listener&lt;/span&gt;:
    &lt;span style=&#34;color:#f92672&#34;&gt;command&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;opt/ros/foxy/bin/ros2 launch demo_nodes_cpp talker_listener.launch.py&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;plugs&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;network, network-bind]&lt;/span&gt;
    &lt;span style=&#34;color:#f92672&#34;&gt;extensions&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;ros2-foxy]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This part is interesting, and even if you&amp;rsquo;ve built snaps in the past there might be something new here. When we build this snap, it will include a complete ROS 2 system: &lt;code&gt;rclcpp&lt;/code&gt;, the &lt;code&gt;demo_nodes_cpp&lt;/code&gt; workspace, etc. It could contain the entire system necessary for a robot in one installable blob. It&amp;rsquo;s a standalone unit: we&amp;rsquo;re in total control of how we want our users to interact with it. We exercise that control via the &lt;code&gt;apps&lt;/code&gt; keyword, where we expose specific commands to the user. Here we specify that this snap has a single app called &lt;code&gt;ros2-talker-listener&lt;/code&gt; (the same name as the snap itself), although it could be anything. The command that this app actually runs within the snap uses the &lt;code&gt;ros-foxy-ros2launch&lt;/code&gt; that we staged to fire up the demo nodes&#39; talker/listener launch file. We use &lt;code&gt;plugs&lt;/code&gt; to specify that this app requires network access (&lt;a href=&#34;https://snapcraft.io/docs/interface-management&#34;&gt;read more about interfaces&lt;/a&gt;). Finally, the new bit: we specify that this uses the &lt;code&gt;ros2-foxy&lt;/code&gt; extension. This is where a significant chunk of the magic happens, wrapping the app in the environment necessary to run ROS 2 Foxy stuff, etc. Every app that needs ROS 2 will need to use that extension, and those that don&amp;rsquo;t need ROS 2 don&amp;rsquo;t need the extension (&lt;a href=&#34;https://snapcraft.io/docs/snapcraft-extensions&#34;&gt;read more about snapcraft extensions&lt;/a&gt;).&lt;/p&gt;
&lt;h3 id=&#34;build-the-snap&#34;&gt;Build the snap&lt;/h3&gt;
&lt;p&gt;Now that we&amp;rsquo;ve defined the &lt;code&gt;snapcraft.yaml&lt;/code&gt;, it&amp;rsquo;s time to build the snap. Make sure you&amp;rsquo;re in the directory we created earlier (the one that contained the &lt;code&gt;snap/&lt;/code&gt; directory), and run &lt;code&gt;snapcraft&lt;/code&gt; with the &lt;code&gt;--enable-experimental-extensions&lt;/code&gt; flag (I told you this was experimental):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/ros2-snap
$ snapcraft --enable-experimental-extensions
*EXPERIMENTAL* extensions enabled.
*EXPERIMENTAL* extension &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ros2-foxy&amp;#39;&lt;/span&gt; enabled.
&amp;lt;snip&amp;gt;
Snapped ros2-talker-listener_0.1_amd64.snap
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that depending on your host and whether or not you&amp;rsquo;ve built snaps in the past, the snapcraft CLI may prompt you to install Multipass, a tool used by the snapcraft CLI to manage VMs for building snaps (rather than building straight on your host).&lt;/p&gt;
&lt;p&gt;The build process will take a few minutes. You&amp;rsquo;ll see the snapcraft CLI install the build dependencies of the demo nodes, build the packages in the workspace and install them into the snap, and fetch their runtime dependencies and unpack them into the snap as well. This is a bit different than previous ROS snap build processes that used &lt;code&gt;rosdep&lt;/code&gt;: this uses &lt;code&gt;catkin_pkg&lt;/code&gt; directly to avoid &lt;code&gt;rosdep&lt;/code&gt;&amp;rsquo;s issue of unpacking even build dependencies into the snap, which means ROS snaps using extensions will be significantly smaller than in the past (and take less time to rebuild). Anyway, at the end, you&amp;rsquo;ll have your snap.&lt;/p&gt;
&lt;h3 id=&#34;test-the-snap&#34;&gt;Test the snap&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s install the snap we just built:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/ros2-snap
$ sudo snap install ros2-talker-listener_0.1_amd64.snap --dangerous
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note the use of the &lt;code&gt;--dangerous&lt;/code&gt; flag. That&amp;rsquo;s required because we&amp;rsquo;re installing a snap from disk instead of using the store, and snapd (the daemon with which we&amp;rsquo;re communicating using the &lt;code&gt;snap&lt;/code&gt; command) only trusts snaps that it can cryptographically verify as being from the store unless we tell it otherwise with this flag.&lt;/p&gt;
&lt;p&gt;Finally, let&amp;rsquo;s run the app we defined in the &lt;code&gt;snapcraft.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ ros2-talker-listener
&amp;lt;snip&amp;gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;talker-1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: process started with pid &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;21071&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;listener-2&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: process started with pid &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;21073&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;talker-1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1597444182.799512859&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;talker&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Publishing: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello World: 1&amp;#39;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;listener-2&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1597444182.800671590&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;listener&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: I heard: &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;Hello World: 1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;talker-1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1597444183.799313385&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;talker&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Publishing: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello World: 2&amp;#39;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;listener-2&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1597444183.799922986&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;listener&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: I heard: &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;Hello World: 2&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;CTRL+C to stop that. As you can see, it works great! You could hand this snap to anyone with a snap-capable system, even if they don&amp;rsquo;t have ROS installed, and it would work exactly the same way for them.&lt;/p&gt;
&lt;p&gt;I hope this is helpful in your snap journey, and gives you a decent guide to building ROS 2 Foxy snaps using the new &lt;code&gt;ros2-foxy&lt;/code&gt; extension. Please feel free to ask any questions here, on the &lt;a href=&#34;https://forum.snapcraft.io/&#34;&gt;Snapcraft forums&lt;/a&gt;, or on the &lt;a href=&#34;https://discourse.ros.org/&#34;&gt;ROS forums&lt;/a&gt;. I&amp;rsquo;d love to hear any feedback you have.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Four years of Nextcloud</title>
      <link>https://kyrofa.com/posts/four-years-of-nextcloud/</link>
      <pubDate>Sat, 27 Jun 2020 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/four-years-of-nextcloud/</guid>
      <description>I&amp;rsquo;m a few weeks late on celebrating Nextcloud&amp;rsquo;s four year anniversary, but I&amp;rsquo;m doing it anyway! My how time flies. I became a part of the ownCloud community back in 2012. Heck, you can still find blog posts about how I ran it on this very blog if you scroll down far enough. Then, in 2015, I saw the post about the Western Digital/ownCloud collaboration on what would eventually become the Nextcloud Box.</description>
      <content:encoded>&lt;p&gt;I&amp;rsquo;m a few weeks late on celebrating Nextcloud&amp;rsquo;s four year anniversary, but I&amp;rsquo;m doing it anyway! My how time flies. I became a part of the ownCloud community back in 2012. Heck, you can still find blog posts about how I ran it on this very blog if you scroll down far enough. Then, in 2015, I saw the post about the &lt;a href=&#34;https://owncloud.org/news/western-digital-and-owncloud-team-up-to-bring-owncloud-to-home-users/&#34;&gt;Western Digital/ownCloud&lt;/a&gt; collaboration on what would eventually become the Nextcloud Box. But I&amp;rsquo;m getting ahead of myself.&lt;/p&gt;
&lt;p&gt;The timing was excellent. We at Canonical were busy developing &lt;a href=&#34;https://snapcraft.io/&#34;&gt;Ubuntu Core and snaps&lt;/a&gt;, and I had just recently joined the team responsible for developing snapcraft, the tool that creates snaps. I thought &amp;ldquo;What better way to learn the ins and outs of what I&amp;rsquo;m making than to participate in this competition!&amp;rdquo; So a few ownCloud community members and I worked on &lt;a href=&#34;https://www.mail-archive.com/devel@owncloud.org/msg02257.html&#34;&gt;exactly that&lt;/a&gt;. It was ultimately selected as the platform to ship on the Box, &lt;a href=&#34;https://karlitschek.de/2016/04/big-changes-i-am-leaving-owncloud-inc-today/&#34;&gt;just in time to lose&lt;/a&gt; every &lt;a href=&#34;https://blog.jospoortvliet.com/2016/05/moving-on-from-owncloud.html&#34;&gt;contact I had at ownCloud&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I was disappointed. I assumed the project was dead. Then I heard from my friends, and was told about Nextcloud. And they retained the Western Digital contacts, who now wanted to continue the project with them!&lt;/p&gt;
&lt;p&gt;I was excited. Within days of Nextcloud making their first release I had created a Nextcloud snap. You can still use Nextcloud v9 today, in fact:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install nextcloud --channel&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I&amp;rsquo;ll always have fond memories of those pixelated clouds.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/nextcloud-9-login.jpg&#34; alt=&#34;nextcloud 9 login&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;I was very happy with it. A one-line installation method for a production-ready Nextcloud. Let&amp;rsquo;s Encrypt integration. Everything. Despite being created to enable the Nextcloud Box, we made sure that it worked on all architectures, so folks could run it wherever they wanted to.&lt;/p&gt;
&lt;p&gt;Before too long, the &lt;a href=&#34;https://nextcloud.com/box/&#34;&gt;Nextcloud Box&lt;/a&gt; became a reality, becoming a joint project between Nextcloud, Western Digital Labs, and Canonical (where Canonical was maintaining the image). At that time, we were actually having to patch Nextcloud to make it work the way we needed it to. We had tried upstreaming that patch to ownCloud, but they turned it down. Which means if you look at the config file you&amp;rsquo;ll see a dirty little secret:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/**
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; * Nextcloud doesn&amp;#39;t support hosting the config file out of the application, so
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; * this version of Nextcloud has been patched slightly to allow for it. As a
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; * result, we fail the integrity check, so we&amp;#39;ll just disable it.
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; */&lt;/span&gt;
&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;integrity.check.disabled&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Which I why I remember the Nextcloud v10 release so well: &lt;a href=&#34;https://github.com/nextcloud/server/pull/1081&#34;&gt;Nextcloud didn&amp;rsquo;t have the same problem with that patch&lt;/a&gt;, and as of v10 we no longer needed to patch it, and could re-enable the integrity check!&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install nextcloud --channel&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/nextcloud-10.jpg&#34; alt=&#34;nextcloud 10 &#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;That was a solid release, but it wasn&amp;rsquo;t long before v11 was released with all sorts of security features and a preview of what was to become Nextcloud Talk in a few years:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install nextcloud --channel&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There was a lot of similarity between versions 9, 10, and 11, but a ton of new development went into version 12. That was great, but it also meant that a number things broke in version 12. Snaps automatically update, and this is when we really took &lt;em&gt;ownership&lt;/em&gt; of that responsibility. We decided we weren&amp;rsquo;t cool automatically breaking people. We&amp;rsquo;ve continued that attitude today, so if you&amp;rsquo;ve wondered why the snap lags upstream occasionally, that&amp;rsquo;s why. Anyway, eventually the issues were resolved, and we got everyone onto v12:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install nextcloud --channel&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;12&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This release finally got rid of that dual menu setup (app menu on the left, settings/users on the right) and unified them really nicely:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/nextcloud-12.jpg&#34; alt=&#34;nextcloud 12&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;Version 12 is still one of my favorite releases simply because no new major releases came out for almost a year. Sadly, it was also during this timeframe that Western Digital Labs shut down, thus ending the Nextcloud Box, and thus officially turning the Nextcloud snap into a personal project for me. Despite that, we settled into a groove of maintenance, and our userbase (and group of testers, who are amazing) grew significantly.&lt;/p&gt;
&lt;p&gt;Version 13 saw quite a leap in functionality and design:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install nextcloud --channel&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;13&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For example, this is where Nextcloud Talk officially made its debut. It&amp;rsquo;s also where they got rid of those pixelated clouds and replaced them with a nice abstract banner:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/nextcloud-13-login.jpg&#34; alt=&#34;nextcloud 13 login&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;It was only a few months before the update drum started for v14, which came with all sorts of stuff like SMS 2FA and GDPR compliance:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install nextcloud --channel&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;14&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nextcloud 15 took that even further:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install nextcloud --channel&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;15&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is the release that added support for my beloved Yubikey, U2F, and TOTP. This is also where they introduced Nextcloud Social for a federated social network.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/nextcloud-15-2fa.jpg&#34; alt=&#34;nextcloud 15 2fa&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;Nextcloud 16 came with a much-improved Nextcloud Talk, as well as more fine-grained permissions by way of an access control list:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install nextcloud --channel&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Version 17 is where the remote wipe functionality made its debut, and the shiny new Text app was introduced as the default text editor:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install nextcloud --channel&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;17&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/nextcloud-17-text-editor.jpg&#34; alt=&#34;nextcloud 17 text editor&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;Nextcloud 18 saw a slight rebranding. They started calling it Nextcloud Hub, and made it super easy to preinstall &amp;ldquo;recommended&amp;rdquo; apps, which included Only Office for collaborative document editing:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install nextcloud --channel&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;18&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And now we come to current day, version 19, where they switched to Collabora for collaborative document editing instead of Only Office:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install nextcloud --channel&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;19&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/nextcloud-19-collabora.jpg&#34; alt=&#34;nextcloud 19 collabora&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;Along with Nextcloud 19, we at Canonical have started the &lt;a href=&#34;https://ubuntu.com/appliance&#34;&gt;Ubuntu Appliance portfolio&lt;/a&gt;, which &lt;a href=&#34;https://ubuntu.com/appliance/nextcloud&#34;&gt;includes Nextcloud&lt;/a&gt;. This is essentially a bring-your-own-box Nextcloud Box: an image with the Nextcloud snap already on there, waiting to be flashed onto a Raspberry Pi or a NUC or something. As soon as it&amp;rsquo;s flashed and booted, you have a Nextcloud install.&lt;/p&gt;
&lt;p&gt;Anyway, I hope this brief walk through history was interesting. Note that all the snap tracks used here will be open forever, so you&amp;rsquo;re welcome to use even the first Nextcloud, but as a general rule of thumb, only the two most recent major releases are actually supported and receive updates. Of course, if you just want the latest stable version, you don&amp;rsquo;t need to specify a track at all:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install nextcloud
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s been a good four years. Here&amp;rsquo;s to many more!&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>PSA for ROS users: Some things to know as Python 2 approaches EOL</title>
      <link>https://kyrofa.com/posts/psa-for-ros-users-some-things-to-know-as-python-2-approaches-eol/</link>
      <pubDate>Wed, 23 Oct 2019 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/psa-for-ros-users-some-things-to-know-as-python-2-approaches-eol/</guid>
      <description>We got an interesting question from a customer today, and I think the answer might be helpful to a wider audience. Python 2 will reach end of life in two months. This shouldn&amp;rsquo;t be news to anyone who hasn&amp;rsquo;t been living under a rock, and plans are in place to use Python 3 in Noetic (whereas ROS 2 has always used Python 3). However, the question from our customer was this: What does that mean for existing ROS 1 distributions (Kinetic and Melodic)?</description>
      <content:encoded>&lt;p&gt;We got an interesting question from a customer today, and I think the answer might be helpful to a wider audience. Python 2 will &lt;a href=&#34;https://pythonclock.org/&#34;&gt;reach end of life in two months&lt;/a&gt;. This shouldn&amp;rsquo;t be news to anyone who hasn&amp;rsquo;t been living under a rock, and &lt;a href=&#34;https://discourse.ros.org/t/planning-future-ros-1-distribution-s&#34;&gt;plans are in place&lt;/a&gt; to use Python 3 in Noetic (whereas ROS 2 has always used Python 3). However, the question from our customer was this: &lt;strong&gt;What does that mean for existing ROS 1 distributions (Kinetic and Melodic)?&lt;/strong&gt; They are still using (and will continue to use) Python 2.&lt;/p&gt;
&lt;p&gt;The answer really depends on where you&amp;rsquo;re getting Python 2. &lt;strong&gt;Tl;dr: If you&amp;rsquo;re using Ubuntu Xenial (16.04) or Bionic (18.04), please know that Python 2 from the Ubuntu repositories will continue to be supported for the lifetime of the Ubuntu release, regardless of Python 2&amp;rsquo;s upstream support status.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Ubuntu&amp;rsquo;s &lt;a href=&#34;https://help.ubuntu.com/community/Repositories&#34;&gt;package repositories&lt;/a&gt; are split into a number of components: main, restricted, universe, and multiverse. You can read that link to learn all about them, but let me briefly quote it so you can understand the difference between main and universe:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;main:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The main component contains applications that are free software, can be freely redistributed and are fully supported by the Ubuntu team. [&amp;hellip;] When you install software from the main component, you are assured that the software will come with security updates and that commercial technical support is available from Canonical.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;universe:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The universe component is a snapshot of the free, open-source, and Linux world. [&amp;hellip;] Canonical does not provide a guarantee of regular security updates for software in the universe component, but will provide these where they are made available by the community.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;All Ubuntu releases prior to Eoan (19.10) included Python 2 in main. In Eoan it was demoted to universe. This means that for Xenial (16.04) and Bionic (18.04), Python 2 falls under the main guarantees quoted above. It will continue to be fully-supported and receive free security updates.&lt;/p&gt;
&lt;p&gt;In other words, as long as you&amp;rsquo;re running Kinetic or Melodic against its recommended Ubuntu release targets, you should be golden.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re having &lt;a href=&#34;https://discourse.ros.org/t/some-things-to-know-as-python-2-approaches-eol/11175?u=kyrofa&#34;&gt;a discussion&lt;/a&gt; about this in the ROS forum if you&amp;rsquo;d like to participate.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Robotics security: What is SROS 2?</title>
      <link>https://kyrofa.com/posts/robotics-security-what-is-sros-2/</link>
      <pubDate>Fri, 04 Oct 2019 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/robotics-security-what-is-sros-2/</guid>
      <description>We at Canonical have been hard at work on the security features of version 2 of the Robot Operating System (ROS 2). However, if we lift our collective heads up out of the weeds it&amp;rsquo;s easy to see folks completely misunderstanding how security works today in ROS 2. We&amp;rsquo;ve written some design articles to help distill all the moving pieces into something comprehensible, but I wanted to do the same here in a slightly less formal way.</description>
      <content:encoded>&lt;p&gt;We at Canonical have been hard at work on the security features of version 2 of the Robot Operating System (ROS 2). However, if we lift our collective heads up out of the weeds it&amp;rsquo;s easy to see folks completely misunderstanding how security works today in ROS 2. We&amp;rsquo;ve written some &lt;a href=&#34;http://design.ros2.org/articles/ros2_dds_security.html&#34;&gt;design articles&lt;/a&gt; to help distill all the moving pieces into something comprehensible, but I wanted to do the same here in a slightly less formal way.&lt;/p&gt;
&lt;p&gt;ROS 2 uses &lt;a href=&#34;https://www.omg.org/spec/DDS/1.4/PDF&#34;&gt;DDS&lt;/a&gt;, an existing protocol with its own specs. It&amp;rsquo;s out of scope to explain DDS, but there are two important points to make for the purpose of this post:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;DDS is just a spec; there are various DDS vendors that create both open source and commercial DDS implementations.&lt;/li&gt;
&lt;li&gt;DDS supports various security features by way of its &lt;a href=&#34;https://www.omg.org/spec/DDS-SECURITY/1.1/PDF&#34;&gt;DDS-Security spec&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Both of those specs are just as much fun to read as any other spec, so let&amp;rsquo;s paint a simplified picture of how DDS-Security works.&lt;/p&gt;
&lt;h2 id=&#34;what-is-dds-security&#34;&gt;What is DDS-Security?&lt;/h2&gt;
&lt;p&gt;DDS-Security is, again, just a spec, and an optional one at that (not all DDS implementations support it). It expands upon DDS by defining a set of plugins with specific purposes and APIs, as well as defining a set of built-in plugin implementations that must be supported by a given DDS implementation in order to be considered in compliance with the spec. It defines five plugins:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Authentication&lt;/strong&gt;: Verify the identity of a given domain participant&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access control&lt;/strong&gt;: Enforce restrictions on the DDS-related operations that can be performed by a participant&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cryptography&lt;/strong&gt;: Handle all required encryption/signing/hashing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logging&lt;/strong&gt;: Provide ability to audit DDS-Security-related events&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data tagging&lt;/strong&gt;: Provide the ability to add tags to data samples&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;ROS 2&amp;rsquo;s security features only utilize the first three of these plugins. Why? Because those last two aren&amp;rsquo;t required in order to be compliant with the spec, and thus not all DDS implementations support them. We need to cater to the lowest common denominator, here. Let&amp;rsquo;s talk a little more about those first three plugins.&lt;/p&gt;
&lt;h3 id=&#34;authentication&#34;&gt;Authentication&lt;/h3&gt;
&lt;p&gt;The &lt;strong&gt;Authentication&lt;/strong&gt; plugin is central to the entire plugin architecture defined by DDS-Security. It provides the concept of a confirmed identity without which further enforcement would be impossible (e.g. it would be awfully hard to make sure a given ROS node could only access specific topics if it was impossible to securely determine which node it was).&lt;/p&gt;
&lt;p&gt;The builtin plugin (which is what ROS 2 uses) uses the proven Public Key Infrastructure (PKI). It requires a public and private key per domain participant, as well as an x.509 certificate that binds the participant&amp;rsquo;s public key to a specific name. Each x.509 certificate must be signed by (or have a signature chain to) a specific Certificate Authority (CA) that the plugin is configured to trust.&lt;/p&gt;
&lt;h3 id=&#34;access-control&#34;&gt;Access control&lt;/h3&gt;
&lt;p&gt;The &lt;strong&gt;Access control&lt;/strong&gt; plugin deals with defining and enforcing restrictions on the DDS-related capabilities of a given domain participant. It&amp;rsquo;s not apparmor or cgroups, it&amp;rsquo;s really about limiting a particular participant to a specific DDS domain, or only allowing the participant to read from or write to specific DDS topics, etc.&lt;/p&gt;
&lt;p&gt;The builtin plugin (which is again what ROS 2 uses) also uses PKI. It requires two files per domain participant:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Governance&lt;/strong&gt; file: A signed XML document specifying how the domain should be secured&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt; file: A signed XML document containing the permissions of the domain participant, bound to the name of the participant as defined by the authentication plugin&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both of these files must be signed by a CA which the plugin is configured to trust. This may be the same CA as the &lt;strong&gt;Authentication&lt;/strong&gt; plugin trusts, but that isn&amp;rsquo;t required.&lt;/p&gt;
&lt;h3 id=&#34;cryptographic&#34;&gt;Cryptographic&lt;/h3&gt;
&lt;p&gt;The &lt;strong&gt;Cryptographic&lt;/strong&gt; plugin is where all the cryptography-related operations are handled: encryption, decryption, signing, hashing, etc. Both the &lt;strong&gt;Authentication&lt;/strong&gt; and &lt;strong&gt;Access control&lt;/strong&gt; plugins utilize this plugin to verify signatures, etc. This is also where the functionality to encrypt DDS topic communication resides.&lt;/p&gt;
&lt;h2 id=&#34;so-what-is-sros-2-its-ros-2s-integration-with-dds-security&#34;&gt;So what is SROS 2? It&amp;rsquo;s ROS 2&amp;rsquo;s integration with DDS-Security&lt;/h2&gt;
&lt;p&gt;SROS 2 stands for &amp;ldquo;Secure Robot Operating System 2.&amp;rdquo; Understandably, this tends to make folks assume it&amp;rsquo;s some sort of ROS 2 fork that is somehow secure, and that&amp;rsquo;s not actually the case. The name is historical: SROS was an effort back in ROS 1 to lock it down, and it was essentially a fork. In ROS 2, the term &amp;ldquo;SROS 2&amp;rdquo; is given to the collective set of features and tools in various parts of ROS 2 that are used to enable integration with DDS-Security. So what is that &amp;ldquo;set of features and tools&amp;rdquo;?&lt;/p&gt;
&lt;h3 id=&#34;ros-client-library-rcl&#34;&gt;ROS client library (RCL)&lt;/h3&gt;
&lt;p&gt;Most of the user-facing runtime support for SROS 2 is contained within the &lt;a href=&#34;https://github.com/ros2/rcl&#34;&gt;ROS Client Library&lt;/a&gt;, a core component of ROS 2. It&amp;rsquo;s responsible for coordinating the enablement of DDS-Security for each DDS implementation that supports it. It supports three main features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Security files for each domain participant&lt;/li&gt;
&lt;li&gt;Permissive and strict enforcement options&lt;/li&gt;
&lt;li&gt;A master on/off switch for all security features&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more details on each of these please read the &lt;a href=&#34;http://design.ros2.org/articles/ros2_dds_security.html&#34;&gt;ROS 2 DDS-Security article&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;sros-2-utilities&#34;&gt;SROS 2 utilities&lt;/h3&gt;
&lt;p&gt;Configuring a ROS 2 system to be secure in RCL involves a lot of technology that may be unfamiliar to the average roboticist (PKI, DDS governance and permissions files and their syntax, etc.). The SROS 2 &lt;a href=&#34;https://github.com/ros2/sros2&#34;&gt;utilities&lt;/a&gt; includes the &lt;strong&gt;ros2 security&lt;/strong&gt; tool to help set up security in a way that RCL can use. It includes the following features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create CAs for the &lt;strong&gt;Authentication&lt;/strong&gt; and &lt;strong&gt;Access control&lt;/strong&gt; plugins&lt;/li&gt;
&lt;li&gt;Create a directory tree containing all security files (keypairs, governance and permissions files, etc.)&lt;/li&gt;
&lt;li&gt;Create new identity for a given node instance, generating a keypair and signing its x.509 certificate&lt;/li&gt;
&lt;li&gt;Create a governance file that will encrypt all DDS traffic by default&lt;/li&gt;
&lt;li&gt;Support specifying permissions in familiar ROS terms which are then automatically converted into low-level DDS permissions&lt;/li&gt;
&lt;li&gt;Support automatically discovering required permissions from a running ROS system&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I hope that helps clear up any misunderstandings you&amp;rsquo;ve had about what SROS 2 is and how it works today. We&amp;rsquo;re always here to help if you have any questions!&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Canonical joins the ROS 2 Technical Steering Committee</title>
      <link>https://kyrofa.com/posts/canonical-joins-the-ros-2-technical-steering-committee/</link>
      <pubDate>Wed, 28 Aug 2019 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/canonical-joins-the-ros-2-technical-steering-committee/</guid>
      <description>We at Canonical care deeply about robotics. We firmly believe that robots based on Linux are cheaper to develop, more flexible, more secure, and faster to market. One of the contributing factors to this being the case is the Robot Operating System (ROS). ROS is by far the most popular middleware for creating Linux-powered robots. It provides all sorts of open source tools and libraries and pre-made components that solve common problems encountered during robot development.</description>
      <content:encoded>&lt;p&gt;We at Canonical care deeply about robotics. We firmly believe that robots based on Linux are cheaper to develop, more flexible, more secure, and faster to market. One of the contributing factors to this being the case is the &lt;a href=&#34;https://www.ros.org/&#34;&gt;Robot Operating System&lt;/a&gt; (ROS). ROS is by far the most popular middleware for creating Linux-powered robots. It provides all sorts of open source tools and libraries and pre-made components that solve common problems encountered during robot development. This allows roboticists to avoid needing to reinvent the wheel and instead focus on what really makes their robot unique. Of course, another reason we care about ROS is that most of the ROS community use Ubuntu. We love our users, and we want to make sure the experience they have on Ubuntu is consistently stellar!&lt;/p&gt;
&lt;p&gt;We also care deeply about security, and that permeates everything we do. We&amp;rsquo;ve all seen how the IoT wave has been going in this regard: badly. IoT devices are low-margin, and no one has any incentive to keep them up to date or ensure that they&amp;rsquo;re secure in the first place. Manufacturers want to drive costs down, and users don&amp;rsquo;t consider the devices computers and don&amp;rsquo;t give a second thought to connecting them to the internet. It&amp;rsquo;s an unfortunate set of circumstances.&lt;/p&gt;
&lt;p&gt;We think that the best way out of this situation is to make security and maintenance so easy that it becomes the obvious choice. If it was suddenly easier and cheaper for device manufacturers to create secure devices that can be automatically updated, why wouldn&amp;rsquo;t they do it? That&amp;rsquo;s the premise behind snaps and Ubuntu Core: by making complex topics like security and updates transparent and straightforward, we can make the entire ecosystem better for everyone.&lt;/p&gt;
&lt;p&gt;Now, we aren&amp;rsquo;t quite that naive. A secure and robust operating system and packaging and update process are only pieces of a puzzle. If the software running on the devices has security holes, the picture is incomplete. Which brings us back to robotics and ROS.&lt;/p&gt;
&lt;p&gt;Robotics is a realm of experimentation: evaluating different hardware and software, pushing ahead with what works, and culling what doesn&amp;rsquo;t. ROS 1, the version used by most production robots, was designed to be flexible to enable this experimentation. The flexibility of allowing existing components to be easily combined with new ones or swapped with others was valued above all else, at the cost of security.&lt;/p&gt;
&lt;p&gt;We haven&amp;rsquo;t quite seen a wave of robots like we&amp;rsquo;ve seen with IoT, but we&amp;rsquo;re pretty sure it&amp;rsquo;s coming, and we want to make sure it doesn&amp;rsquo;t suffer the same fate. Just like IoT, the best way to make sure a production robot is secure is to make security as easy and transparent as possible. This is why we&amp;rsquo;re so interested in ROS 2.&lt;/p&gt;
&lt;p&gt;ROS 2 is undergoing active development and is being built with the flexibility of ROS 1 while also supporting the technology necessary to secure it at its very core. A while ago, we joined the effort of fleshing out the ROS 2 security story; we&amp;rsquo;ve been helping with the design as well as software implementation. In order to effectively coordinate this work as well as show our commitment to it, we&amp;rsquo;re pleased to announce that we have become a member of the &lt;a href=&#34;https://discourse.ros.org/t/10417&#34;&gt;ROS 2 Technical Steering Committee&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re genuinely excited to be a part of such an open source powerhouse, and look forward to bringing our security expertise to bear as ROS 2 matures. With the right push during its development, we&amp;rsquo;re convinced ROS 2 will develop into an ecosystem where security comes naturally.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>A better way to review GitHub pull requests for snaps</title>
      <link>https://kyrofa.com/posts/a-better-way-to-review-github-pull-requests-for-snaps/</link>
      <pubDate>Fri, 03 May 2019 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/a-better-way-to-review-github-pull-requests-for-snaps/</guid>
      <description>I&amp;rsquo;ve had a very particular itch for a long time, now. Something I love about open source is that it empowers developers to scratch their own itches, and a while back I decided to do exactly that. We&amp;rsquo;ve used it in production on the Nextcloud snap for a few months with relatively few issues, so I think it&amp;rsquo;s time to open it up for wider use&amp;hellip; in case you share my itch.</description>
      <content:encoded>&lt;p&gt;I&amp;rsquo;ve had a very particular itch for a long time, now. Something I love about open source is that it empowers developers to scratch their own itches, and a while back I decided to do exactly that. We&amp;rsquo;ve used it in production on the Nextcloud snap for a few months with relatively few issues, so I think it&amp;rsquo;s time to open it up for wider use&amp;hellip; in case you share my itch. What is open source but a bunch of users scratching togeth&amp;hellip;. okay this analogy is gross.&lt;/p&gt;
&lt;h2 id=&#34;the-problem&#34;&gt;The problem&lt;/h2&gt;
&lt;p&gt;What is the problem that has annoyed me for years? It&amp;rsquo;s that &lt;strong&gt;reviewing pull requests for snaps sucks&lt;/strong&gt;. It involves too much manual work. Sure, it&amp;rsquo;s relatively easy to automatically verify that the snap builds, as long as it&amp;rsquo;s a snap that actually builds within the Travis or CircleCI timeout. Then what? How do you verify that it actually &lt;em&gt;works&lt;/em&gt;?&lt;/p&gt;
&lt;p&gt;Testing that a snap actually works obviously depends on the snap in question. Nextcloud is a web application, so we actually have an entire &lt;a href=&#34;https://github.com/nextcloud/nextcloud-snap/tree/master/tests&#34;&gt;suite of Capybara-powered tests&lt;/a&gt; to automatically verify the snap is sane. In some cases, that&amp;rsquo;s enough. For example, if a pull request is made that updates PHP, I can be relatively confident that none of those tests would pass if that PHP release was broken.&lt;/p&gt;
&lt;p&gt;However, there are a whole host of things that can&amp;rsquo;t be automated. For Nextcloud, want to test that the Let&amp;rsquo;s Encrypt integration still works correctly? That&amp;rsquo;s all manual. What if it&amp;rsquo;s a GUI application? Those are a lot harder to fully test automatically. Besides, what if the PR changed the GUI itself? You probably want to take a look at it. Basically, you must install the snap.&lt;/p&gt;
&lt;p&gt;Sadly, even if you build a snap as part of your CI, it&amp;rsquo;s usually lost after that ephemeral process. I mean, you can upload it to some sort of external service after it was built, but I&amp;rsquo;ve had mixed success with that, and it&amp;rsquo;s work. Besides, we already &lt;em&gt;have&lt;/em&gt; one of those&amp;ndash; the snap store! Why can&amp;rsquo;t we just upload there? Because it involves a very private token, which would need to be exposed on pull requests to the code random people submit, which means anyone can just steal your private token and start releasing snaps your behalf. I hope you agree that such a thing would be bad.&lt;/p&gt;
&lt;p&gt;That turns the typical workflow for reviewing pull requests into this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Code review&lt;/li&gt;
&lt;li&gt;Pull the code&lt;/li&gt;
&lt;li&gt;Build a snap locally&lt;/li&gt;
&lt;li&gt;Test the snap locally&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For every pull request. That&amp;rsquo;s actually a lot of work and a lot of time, and it meant that the snap moved quite a bit slower than I would have liked. I was always annoyed having to review pull requests (building the Nextcloud snap takes an &lt;em&gt;hour&lt;/em&gt;), and I don&amp;rsquo;t want to be annoyed just because someone is trying to contribute! What a terrible attitude.&lt;/p&gt;
&lt;p&gt;So I was on vacation a few months ago, and hacked together something that solved this problem, something that built and released snaps for every pull request without handing my token out to anyone who wanted it. I then reached &lt;em&gt;deep&lt;/em&gt; into my imagination and came up with the name &lt;a href=&#34;https://github.com/kyrofa/github-snap-builder&#34;&gt;GitHub Snap Builder&lt;/a&gt;. This project is in beta and is far from finished (heck, it still has the default README). However, it&amp;rsquo;s in a usable state, and I wanted to write this blog post to get you started using it, if you&amp;rsquo;re interested. Let&amp;rsquo;s dive in, shall we?&lt;/p&gt;
&lt;h2 id=&#34;the-solution&#34;&gt;The solution&lt;/h2&gt;
&lt;p&gt;GitHub Snap Builder (it just rolls off the tongue, right?) is a hosted solution. It runs on your own server, and you connect it to a &lt;a href=&#34;https://developer.github.com/apps/about-apps/&#34;&gt;GitHub App&lt;/a&gt; that you create in your organization (or under your user account). This allows it to run like any other CI system (e.g. Travis), report status on pull requests, etc. Let&amp;rsquo;s start by getting that GitHub App setup.&lt;/p&gt;
&lt;h3 id=&#34;github-app&#34;&gt;GitHub App&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;ll walk you through creating a GitHub App under your personal account, but doing the same for an organization is very similar. Let&amp;rsquo;s work off of the &lt;a href=&#34;https://developer.github.com/apps/building-github-apps/creating-a-github-app/&#34;&gt;Creating a GitHub App&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;First, go to your profile settings, then to &lt;strong&gt;Developer settings&lt;/strong&gt; in the left sidebar. From there click &lt;strong&gt;GitHub Apps&lt;/strong&gt; in the left sidebar, and then hit the &lt;strong&gt;New GitHub App&lt;/strong&gt; button.&lt;/p&gt;
&lt;p&gt;Give it a name, and set the Homepage URL to whatever you want. The Webhook URL needs to be pointing at the GitHub Snap Builder, so use the URL of the server, e.g. &lt;code&gt;http://domain-name.com&lt;/code&gt;. I know we haven&amp;rsquo;t talked about this yet, we&amp;rsquo;ll revisit it, just get something in there so you can create the app.&lt;/p&gt;
&lt;p&gt;I know the Webhook secret says it&amp;rsquo;s optional, but if you&amp;rsquo;re using the GitHub Snap Builder (so fun to say) it&amp;rsquo;s not. Generate your own password, make it good, and put it in there. Keep track of it, you&amp;rsquo;ll need it in a minute.&lt;/p&gt;
&lt;p&gt;Now for the permissions. GitHub Snap Builder requires the ability to &lt;strong&gt;read&lt;/strong&gt; pull requests, and &lt;strong&gt;read/write&lt;/strong&gt; commit statuses ( the place where your CI systems report success/failure). Under &lt;strong&gt;Permissions&lt;/strong&gt;, scroll down until you see &lt;strong&gt;Pull requests&lt;/strong&gt; and select &lt;strong&gt;Read-only&lt;/strong&gt; in the drop-down. In that same section, find &lt;strong&gt;Commit statuses&lt;/strong&gt; and select &lt;strong&gt;Read &amp;amp; write&lt;/strong&gt; in the drop-down. Then continue scrolling down until you see the &lt;strong&gt;Subscribe to events&lt;/strong&gt; section, and check the &lt;strong&gt;Pull request&lt;/strong&gt; box (so GitHub Snap Builder is notified whenever a pull request is opened or updated).&lt;/p&gt;
&lt;p&gt;Finally, hit the big green &lt;strong&gt;Create GitHub App&lt;/strong&gt; button.&lt;/p&gt;
&lt;p&gt;Now that your application is created, you need to do one more thing. Scroll down to the bottom of the page you&amp;rsquo;re on now (your new application) and you&amp;rsquo;ll see a big green &lt;strong&gt;Generate a private key&lt;/strong&gt; button. Hit that. It&amp;rsquo;ll immediately download a private key that you&amp;rsquo;ll need in a minute. Keep the app page open, and move on to setting up your server.&lt;/p&gt;
&lt;h3 id=&#34;server-setup&#34;&gt;Server setup&lt;/h3&gt;
&lt;p&gt;GitHub Snap Builder is written as a Ruby gem. As such, you can consume it in two ways. First of all, you can install the gem:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ gem install github_snap_builder
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or you can install the snap:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install github-snap-builder --beta --classic
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;re going to use the snap since it sets up the daemons and whatnot for us, but this post should still be useful for you even if you opt for the gem.&lt;/p&gt;
&lt;p&gt;Once you install the snap, a daemon will be running. So how does this work? That daemon sits and waits for GitHub to tell it that a pull request event has happened (e.g. a pull request was created or updated). When that request comes in, GitHub Snap Builder will fire up an ephemeral container, build the snap, toast the container, create a new ephemeral container, push/release it to the proper channel, and toast the container again. Note the use of two different containers: it&amp;rsquo;s so we can&amp;rsquo;t possibly be running code from the pull request when we whip out the credentials necessary to upload the snap. GitHub Snap Builder was written so that the container tech is easily extended, but the only supported option today is Docker, so let&amp;rsquo;s install that:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt install docker.io
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Why Docker, by the way? Because I have this thing deployed in a LXD container, and nested LXD containers have problems with snaps, but can run Docker just fine. Anyway, let&amp;rsquo;s configure GitHub Snap Builder. The following command will open up a YAML file in an editor:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo github-snap-builder.configure
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;ll see all sorts of options here with hopefully-helpful comments. Here&amp;rsquo;s where you need information from the GitHub App you just finished creating. Go to the App page, and grab its &lt;strong&gt;App ID&lt;/strong&gt;. Set &lt;code&gt;gitub_app_id&lt;/code&gt; in the config to that value. Set &lt;code&gt;github_webhook_key&lt;/code&gt; equal to the webhook secret that you generated for the app. Grab the private key you downloaded, and open it up in a text editor. Copy the entire file (&amp;ldquo;BEGIN RSA PRIVATE KEY&amp;rdquo; and everything) and paste it into the &lt;code&gt;github_app_private_key&lt;/code&gt;, formatting like the example given in the config.&lt;/p&gt;
&lt;p&gt;Set &lt;code&gt;build_type&lt;/code&gt; to &amp;ldquo;docker&amp;rdquo;. As I mentioned, nothing else is supported today.&lt;/p&gt;
&lt;p&gt;You can set the bind address/port to whatever you want, but the defaults are given there (0.0.0.0:3000), and I left them alone.&lt;/p&gt;
&lt;p&gt;Finally, let&amp;rsquo;s talk about that &lt;code&gt;repos&lt;/code&gt; section. This is where you tell GitHub Snap Builder what repositories it&amp;rsquo;s authorized to build, what channel should be used for release, and provide the token that gives it permission to do so.&lt;/p&gt;
&lt;p&gt;Each item under the &lt;code&gt;repos&lt;/code&gt; section is a YAML object that is the slug of a given repo (&lt;code&gt;&amp;lt;owner&amp;gt;/&amp;lt;repo name&amp;gt;&lt;/code&gt;). For the Nextcloud snap, that&amp;rsquo;s &lt;code&gt;nextcloud/nextcloud-snap&lt;/code&gt;. Nested under that object are two options: &lt;code&gt;channel&lt;/code&gt; and &lt;code&gt;token&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When GitHub Snap Builder has built a snap, it will release it into a pull-request-specific branch in the store that only lives for 30 days, for example &lt;code&gt;edge/pr-123&lt;/code&gt;. That branch is determined with the channel option, and is &lt;code&gt;&amp;lt;channel&amp;gt;/pr-123&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;token&lt;/code&gt; option is something you&amp;rsquo;ll need to generate on your own machine. Run this command (replacing &lt;code&gt;&amp;lt;snap-name&amp;gt;&lt;/code&gt; with the obvious thing and replacing &lt;code&gt;&amp;lt;channel&amp;gt;&lt;/code&gt; with whatever you used for the channel option followed by a &lt;code&gt;/*&lt;/code&gt;, e.g. &lt;code&gt;edge/*&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft export-login&lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;           --snaps&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&amp;lt;snap-name&amp;gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;           --channels&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&amp;lt;channel&amp;gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;           --acls&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;package_push,package_release -
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Yes, note the &lt;code&gt;-&lt;/code&gt; at the end, which will print a token to the screen. Copy that token, and paste it into the &lt;code&gt;token&lt;/code&gt; option, thereby giving permission for the GitHub Snap Builder to upload snaps to the set of branches required. You&amp;rsquo;ll need to go through this process for each repo/snap you want GitHub Snap Builder to support building.&lt;/p&gt;
&lt;p&gt;We could stop here. We could make sure the GitHub App was hooked straight up to the GitHub Snap Builder. It would be a simple matter of editing the GitHub App settings and setting the Webhook URL to &lt;code&gt;http://my-server:3000&lt;/code&gt;. However, doing that would send all that data in the clear (including your app password), which is a bad idea.&lt;/p&gt;
&lt;p&gt;I suggest installing nginx and using it as an SSL terminator and proxy for the snap (getting SSL certs is an exercise left to the reader, but I suggest &lt;a href=&#34;https://letsencrypt.org/&#34;&gt;Let&amp;rsquo;s Encrypt&lt;/a&gt;, it&amp;rsquo;s free). The configuration I used was pretty simple:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;server {
   listen *:443;
   ssl on;
   server_name &amp;lt;domain name&amp;gt;;
   ssl_certificate /etc/letsencrypt/live/&amp;lt;domain name&amp;gt;/fullchain.pem;
   ssl_certificate_key /etc/letsencrypt/live/&amp;lt;domain name&amp;gt;/privkey.pem;

   # teminate SSL and proxy to the actual internal web service.
   location / {
       proxy_pass       http://localhost:3000;
       proxy_set_header Host $host;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header X-Forwarded-Proto $scheme;
       proxy_set_header X-Forwarded-Ssl on; # Optional
       proxy_set_header X-Forwarded-Port $server_port;
       proxy_set_header X-Forwarded-Host $host;
   }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At this point, let&amp;rsquo;s revisit the GitHub App settings, and make sure the Webhook URL hits that nginx endpoint. With that config, it ends up being &lt;code&gt;https://my-server&lt;/code&gt; and that&amp;rsquo;s it.&lt;/p&gt;
&lt;h3 id=&#34;activate-github-app-on-github-repo&#34;&gt;Activate GitHub App on GitHub repo&lt;/h3&gt;
&lt;p&gt;You already configured GitHub Snap Builder to build a specific repo when pull requests happen, but now you need to go install your GitHub app on that specific repo to complete the circle and actually start sending events. On the GitHub App page, hit the &lt;strong&gt;Install App&lt;/strong&gt; item in the left sidebar, and then hit the green &lt;strong&gt;Install&lt;/strong&gt; button next to your username (again, you could do the same for organizations). You&amp;rsquo;ll be given the option to active it for all repositories, or you can select which repositories you have in mind. As long as the repository you configured in the GitHub Snap Builder is among those you select here, it&amp;rsquo;ll ignore the rest and do what you want. Finally hit the &lt;strong&gt;Install&lt;/strong&gt; button, and you&amp;rsquo;re done! Submit a pull request for that repo and watch a snap be built and released. The branch it ends up using will be provided in the status string.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/github-snap-builder.png&#34; alt=&#34;github snap builder&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;Install from that branch and you&amp;rsquo;ll get whatever the most recent change in that pull request produced. As the pull request is updated, GitHub Snap Builder will release updates into the same branch.&lt;/p&gt;
&lt;p&gt;As always, I hope this is useful to you. Feel free to log issues, and pull requests are always welcome!&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Speed up your ROS snap builds</title>
      <link>https://kyrofa.com/posts/speed-up-your-ros-snap-builds/</link>
      <pubDate>Tue, 09 Apr 2019 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/speed-up-your-ros-snap-builds/</guid>
      <description>A while back I wrote a post about distributing a ROS system among multiple snaps. If you want to enable some sort of add-on story, you need to have multiple snaps, and that remains the way to do it today with ROS. That approach works, but I&amp;rsquo;ll be the first to admit that it&amp;rsquo;s not terribly elegant, and we&amp;rsquo;re working on making it better. However, in my experience talking to various ROS users creating snaps, they oftentimes end up using multiple snaps not for an add-on or app store story, but for build speed.</description>
      <content:encoded>&lt;p&gt;A while back I wrote a post about &lt;a href=&#34;https://kyrofa.com/posts/distributing-a-ros-system-among-multiple-snaps&#34;&gt;distributing a ROS system among multiple snaps&lt;/a&gt;. If you want to enable some sort of add-on story, you need to have multiple snaps, and that remains the way to do it today with ROS. That approach works, but I&amp;rsquo;ll be the first to admit that it&amp;rsquo;s not terribly elegant, and we&amp;rsquo;re working on making it better. However, in my experience talking to various ROS users creating snaps, they oftentimes end up using multiple snaps &lt;em&gt;not&lt;/em&gt; for an add-on or app store story, but for build speed. If you&amp;rsquo;re one of those people, keep reading, because there&amp;rsquo;s a better way to do this.&lt;/p&gt;
&lt;p&gt;How can distributing one&amp;rsquo;s ROS system among multiple snaps increase speed? Because if you have a large number of dependencies that don&amp;rsquo;t change very often (e.g. upstream ROS components) you can package them all in their own snap. Then you can use its staging area when building the snap containing your own sweet, sweet code. This bypasses time spent fetching, unpacking, or building dependencies.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s great, and I&amp;rsquo;m happy that pattern has solved issues for folks. However, selecting it is also selecting the extra responsibility and maintenance burden that comes along with it. For example, handling the atomic update and rollback of a single snap is simple, but it quickly gets complex when you&amp;rsquo;re dealing with multiple snaps that need to be in lockstep. Basically, if you don&amp;rsquo;t &lt;em&gt;need&lt;/em&gt; to distribute multiple snaps tied together with content sharing, I recommend avoiding it. Embrace bundling dependencies into one isolated snap that represents your product. Thankfully, there&amp;rsquo;s a way to do both: get the speed benefits of having multiple snaps, while only needing to distribute one.&lt;/p&gt;
&lt;h2 id=&#34;stage-snaps&#34;&gt;&lt;code&gt;stage-snaps&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;ve built a snap in the past, you&amp;rsquo;re probably familiar with the &lt;code&gt;[stage-packages](https://docs.snapcraft.io/snapcraft-parts-metadata/8336)&lt;/code&gt; &lt;a href=&#34;https://docs.snapcraft.io/snapcraft-parts-metadata/8336&#34;&gt;option available to parts&lt;/a&gt;. It&amp;rsquo;s essentially a list of Debian packages for the snapcraft CLI to download, unpack, and distribute along with the rest of the part (i.e. bundle into the snap). As of version 3.2 of the snapcraft CLI, the &lt;code&gt;stage-snaps&lt;/code&gt; option is also available, which offers the same functionality for &lt;em&gt;snaps&lt;/em&gt;. Version 3.4 of the snapcraft CLI adds the functionality required to save as much time as possible when building ROS snaps using &lt;code&gt;stage-snaps&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;What this means is that, instead of needing to maintain a &amp;ldquo;producer&amp;rdquo; snap (which is a runtime dependency), as well as keep hold of its staging area for building a &amp;ldquo;consumer&amp;rdquo;, you can simply build a snap of all your dependencies and then bundle it straight into the &amp;ldquo;consumer&amp;rdquo; and only ship &lt;em&gt;it&lt;/em&gt; as your end-product.&lt;/p&gt;
&lt;p&gt;This is best explained with an example.&lt;/p&gt;
&lt;h2 id=&#34;an-example&#34;&gt;An example&lt;/h2&gt;
&lt;p&gt;Similar to the &lt;a href=&#34;https://kyrofa.com/posts/distributing-a-ros-system-among-multiple-snaps&#34;&gt;post about distributing a ROS system among multiple snaps&lt;/a&gt;, we&amp;rsquo;re going to create two snaps: one to contain common dependencies, and another to be our actual product. The word &amp;ldquo;base&amp;rdquo; has been overloaded by the snap project, so we&amp;rsquo;ll call our dependency snap our &amp;ldquo;foundation&amp;rdquo; and the product snap our &amp;ldquo;app&amp;rdquo; snap.&lt;/p&gt;
&lt;h3 id=&#34;create-ros-foundation&#34;&gt;Create &lt;code&gt;ros-foundation&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Create a &lt;code&gt;snap/snapcraft.yaml&lt;/code&gt; with the following contents:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ros-foundation-kyrofa&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;base&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;core18&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0.1&amp;#39;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;summary&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ROS foundational snap&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;description&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; Contains roscore and basic ROS utilities. Meant for use as a stage-snap in
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; other snaps.&lt;/span&gt; 

&lt;span style=&#34;color:#f92672&#34;&gt;grade&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;stable&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;confinement&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;strict&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;parts&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;ros-foundation&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;catkin&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;source&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;.&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;include-roscore&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;catkin-packages&lt;/span&gt;: []
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;ll notice I called mine &lt;code&gt;ros-foundation-kyrofa&lt;/code&gt; to avoid any name clashes. It&amp;rsquo;s pretty useless on its own: it contains no apps, so it&amp;rsquo;s not like end-users can interact with it. It&amp;rsquo;s really just a blob of stuff. What stuff? &lt;code&gt;roslib&lt;/code&gt;, &lt;code&gt;roscore&lt;/code&gt;, &lt;code&gt;std_msgs&lt;/code&gt;, etc. In this case it doesn&amp;rsquo;t contain any of my own Catkin packages, but it certainly could&amp;ndash; got some libraries that don&amp;rsquo;t change very often? Put them here.&lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;snapcraft&lt;/code&gt; on that, and after a little time you&amp;rsquo;ll have your &amp;ldquo;foundation&amp;rdquo; snap ready for use. Let&amp;rsquo;s get it into the &lt;code&gt;stable&lt;/code&gt; channel of the store:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft register ros-foundation-kyrofa &lt;span style=&#34;color:#75715e&#34;&gt;# You only need to do this once&lt;/span&gt;
$ snapcraft push ros-foundation-kyrofa_0.1_amd64.snap --release&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;stable
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;create-ros-app&#34;&gt;Create &lt;code&gt;ros-app&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The starting point for our &amp;ldquo;app&amp;rdquo; snap is the standard ROS talker/listener snap you&amp;rsquo;ve probably seen a thousand times. We&amp;rsquo;ll take it from the &lt;a href=&#34;https://github.com/snapcore/snapcraft/tree/3.3/tests/spread/plugins/catkin/snaps/ros-talker-listener&#34;&gt;current source tree of the snapcraft CLI&lt;/a&gt; and tweak it a bit.&lt;/p&gt;
&lt;p&gt;At this point, if you were distributing both foundation and app snaps, you&amp;rsquo;d go through the dance of tarring up the foundation staging area, adding a new part to the app snap, etc. We don&amp;rsquo;t need to do that in this case&amp;ndash; our tweak is far simpler. Make the &lt;code&gt;snap/snapcraft.yaml&lt;/code&gt; look something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ros-app-kyrofa&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;base&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;core18&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0.1&amp;#39;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;summary&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ROS app snap&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;description&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; &lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;Contains ROS talker/listener, including foundational ROS stuff from stage-snaps.&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;grade&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;stable&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;confinement&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;strict&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;apps&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;ros-talker-listener&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;command&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;roslaunch listener talk_and_listen.launch&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;plugs&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;network-bind]&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;parts&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;ros-app&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;catkin&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;source&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;.&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;include-roscore&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;stage-snaps&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;ros-foundation-kyrofa]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Other than the addition of the base option, the most important parts are the two lines at the bottom: the addition of the &lt;code&gt;stage-snaps&lt;/code&gt; option, and the specification not to include &lt;code&gt;roscore&lt;/code&gt; (because it&amp;rsquo;s already provided by our &amp;ldquo;foundation&amp;rdquo; snap). In order to see other effects of this new pattern, run &lt;code&gt;snapcraft pull&lt;/code&gt;, which is responsible for downloading all dependencies of the snap:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft pull
&amp;lt;snip... installing build packages&amp;gt;
&amp;lt;snip... fetching roslib&amp;gt;
Downloading snap &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ros-foundation-kyrofa&amp;#39;&lt;/span&gt;
Pulling ros-app
&amp;lt;snip... fetching catkin, compilers, and rosdep&amp;gt;
Initializing rosdep database...
Updating rosdep database...
Determining system dependencies &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; Catkin packages...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. Normally, after &amp;ldquo;determining system dependencies,&amp;rdquo; you&amp;rsquo;d see it move on to actually fetching those dependencies. However, again as of v3.4, the snapcraft CLI is smart enough to realize that those dependencies are already contained within the &amp;ldquo;foundation&amp;rdquo; snap that we included in &lt;code&gt;stage-snaps&lt;/code&gt;, so it doesn&amp;rsquo;t bother to fetch them again.&lt;/p&gt;
&lt;p&gt;Go ahead and run &lt;code&gt;snapcraft&lt;/code&gt; now to build the &amp;ldquo;app&amp;rdquo; snap the rest of the way, and then you can test it out without needing the &amp;ldquo;foundation&amp;rdquo; snap at runtime (since it&amp;rsquo;s bundled within the &amp;ldquo;app&amp;rdquo; snap now):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install ros-app-kyrofa_0.1_amd64.snap --dangerous
ros-app-kyrofa 0.1 installed
$ ros-app-kyrofa.ros-talker-listener
&amp;lt;snip&amp;gt;
NODES
/
listener &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;listener/listener_node&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
talker &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;talker/talker_node&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
auto-starting new master
&amp;lt;snip&amp;gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1554841533.046517090&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1554841533.146675333&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1554841533.246651599&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1554841533.246916&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: I heard Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1554841533.346644894&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1554841533.347463&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: I heard Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, it works just as well as before, but now there&amp;rsquo;s no content sharing involved: just one snap. One snap that could be installed by anyone and work the exact same way.&lt;/p&gt;
&lt;h2 id=&#34;limitations&#34;&gt;Limitations&lt;/h2&gt;
&lt;p&gt;There are, of course, some limitations. For example. when building the &amp;ldquo;app&amp;rdquo; snap, you&amp;rsquo;ll notice that the snapcraft CLI fetches &lt;code&gt;roslib&lt;/code&gt;, even though it&amp;rsquo;s already contained within the &amp;ldquo;foundation.&amp;rdquo; This happens because the snapcraft CLI doesn&amp;rsquo;t define the order in which it handles &lt;code&gt;stage-snaps&lt;/code&gt; and &lt;code&gt;stage-packages&lt;/code&gt;. This does waste some time (at least on the first pull; subsequent pulls should have it cached), and will need to be a future optimization.&lt;/p&gt;
&lt;p&gt;Another limitation is that, oftentimes, dependencies are Debian packages, and there&amp;rsquo;s no simple way to break a Debian package&amp;rsquo;s dependency chain. For example, say your Catkin package depends upon &lt;code&gt;A&lt;/code&gt;. If the stage-snap already contains &lt;code&gt;A&lt;/code&gt;, great, the snapcraft CLI won&amp;rsquo;t fetch it again. However, let&amp;rsquo;s say your Catkin package depends upon &lt;code&gt;B&lt;/code&gt;, which then depends upon &lt;code&gt;A&lt;/code&gt;. In that case, unless the &lt;code&gt;stage-snap&lt;/code&gt; also contains &lt;code&gt;B&lt;/code&gt;, the snapcraft CLI will fetch both &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt;, since the &lt;code&gt;B&lt;/code&gt; Debian package depends upon the &lt;code&gt;A&lt;/code&gt; Debian package, and that&amp;rsquo;s just the way the APT API works. There are some things we can try here, but again, it&amp;rsquo;s a future optimization.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re one of the people trying to ship multiple snaps just because you want your app snaps to build faster, take a step back. Perhaps you could benefit from just using &lt;code&gt;stage-snaps&lt;/code&gt; and only shipping a single snap to your users or devices. It will simplify your life, and you&amp;rsquo;ll still get the speed savings you were craving.&lt;/p&gt;
&lt;p&gt;As always, feel free to ask any questions in the &lt;a href=&#34;https://forum.snapcraft.io/&#34;&gt;snapcraft forum&lt;/a&gt;.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Cipher update for the Nextcloud snap</title>
      <link>https://kyrofa.com/posts/cipher-update-for-the-nextcloud-snap/</link>
      <pubDate>Sun, 17 Mar 2019 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/cipher-update-for-the-nextcloud-snap/</guid>
      <description>Security is a always a balancing act between safety and practicality. If we&amp;rsquo;re talking about the internet, you can go all the way from something impenetrable and completely useless to something easy to use and also part of a botnet. In the case of the Nextcloud snap&amp;rsquo;s SSL cipher suite, as with any website, it&amp;rsquo;s a balance of supporting only the best ciphers while also ensuring that older clients aren&amp;rsquo;t left out in the cold.</description>
      <content:encoded>&lt;p&gt;Security is a always a balancing act between safety and practicality. If we&amp;rsquo;re talking about the internet, you can go all the way from something impenetrable and completely useless to something easy to use and also part of a botnet. In the case of the &lt;a href=&#34;https://snapcraft.io/nextcloud&#34;&gt;Nextcloud snap&lt;/a&gt;&amp;rsquo;s SSL cipher suite, as with any website, it&amp;rsquo;s a balance of supporting only the best ciphers while also ensuring that older clients aren&amp;rsquo;t left out in the cold.&lt;/p&gt;
&lt;p&gt;The current cipher suite was selected several years ago, back when most folks were using Windows 7. We of course scan it periodically with &lt;a href=&#34;https://www.ssllabs.com/ssltest/&#34;&gt;SSL Labs&lt;/a&gt; and &lt;a href=&#34;https://testssl.sh/&#34;&gt;testssl.sh&lt;/a&gt;, and nightly with Nessus. We tweak it periodically to ensure it reflects the current security landscape and gets good results from those tools, but it hasn&amp;rsquo;t really changed that much.&lt;/p&gt;
&lt;p&gt;As I mentioned earlier, one can always make the cipher suite more secure, but the side effect is that older clients who don&amp;rsquo;t support newer/better ciphers will no longer be able to communicate with the server. This is what our config currently looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SSLProtocol all -SSLv3
SSLHonorCipherOrder On
SSLCipherSuite ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This supports a lot of clients. Here&amp;rsquo;s the client simulation from testssl.sh:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Android 2.3.7               No connection
Android 4.1.1               TLSv1.0 ECDHE-RSA-AES256-SHA, 256 bit ECDH (P-256)
Android 4.3                 TLSv1.0 ECDHE-RSA-AES256-SHA, 256 bit ECDH (P-256)
Android 4.4.2               TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Android 5.0.0               TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
Android 6.0                 TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
Android 7.0                 TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Chrome 51 Win 7             TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Chrome 57 Win 7             TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Firefox 49 Win 7            TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Firefox 53 Win 7            TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
IE 6 XP                     No connection
IE 7 Vista                  TLSv1.0 ECDHE-RSA-AES256-SHA, 256 bit ECDH (P-256)
IE 8 XP                     No connection
IE 8 Win 7                  TLSv1.0 ECDHE-RSA-AES256-SHA, 256 bit ECDH (P-256)
IE 11 Win 7                 TLSv1.2 DHE-RSA-AES256-GCM-SHA384, 4096 bit DH
IE 11 Win 8.1               TLSv1.2 DHE-RSA-AES256-GCM-SHA384, 4096 bit DH
IE 11 Win Phone 8.1 Update  TLSv1.2 DHE-RSA-AES256-GCM-SHA384, 4096 bit DH
IE 11 Win 10                TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Edge 13 Win 10              TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Edge 13 Win Phone 10        TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Opera 17 Win 7              TLSv1.2 ECDHE-RSA-AES256-SHA, 256 bit ECDH (P-256)
Safari 5.1.9 OS X 10.6.8    TLSv1.0 ECDHE-RSA-AES256-SHA, 256 bit ECDH (P-256)
Safari 7 iOS 7.1            TLSv1.2 ECDHE-RSA-AES256-SHA384, 256 bit ECDH (P-256)
Safari 9 OS X 10.11         TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Safari 10 OS X 10.12        TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Apple ATS 9 iOS 9           TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Tor 17.0.9 Win 7            TLSv1.0 ECDHE-RSA-AES256-SHA, 256 bit ECDH (P-256)
Java 6u45                   No connection
Java 7u25                   TLSv1.0 ECDHE-RSA-AES128-SHA, 256 bit ECDH (P-256)
Java 8u31                   TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
OpenSSL 1.0.1l              TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
OpenSSL 1.0.2e              TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here&amp;rsquo;s the same from SSL Labs:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/nextcloud-cipher-update-ssllabs-before.png&#34; alt=&#34;ssl labs result before update&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s pretty darn good. Windows XP isn&amp;rsquo;t all that well supported, but heck, Vista is, and 7 and on definitely are. All these clients, and we still were able to get an A+ from SSL Labs.&lt;/p&gt;
&lt;p&gt;Nowadays, though, some of these older clients just aren&amp;rsquo;t as important anymore. XP barely registers, Vista even less, and everyone gets a new phone every year or two (which is silly, by the way). The change in client landscape means it&amp;rsquo;s time to rebalance our cipher list against the clients we care to support. How do we go about determining that?&lt;/p&gt;
&lt;p&gt;I won&amp;rsquo;t pretend I&amp;rsquo;m a security expert. I don&amp;rsquo;t at any one time know which ciphers are best and which are less best, which is why I rely on other folks and tools who do. So I decided to start with the strongest cipher list I could find and see what clients it eliminated. In order to find that list, I went to the &lt;a href=&#34;https://ssl-config.mozilla.org/&#34;&gt;Mozilla SSL config generator&lt;/a&gt; and turned everything up to 11 (i.e. checked the &amp;ldquo;modern&amp;rdquo; radio). It gave me this config:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLHonorCipherOrder on
SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It actually supported a pretty good number of clients, but testssl.sh whined about it in the same way it whined about the current config:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;LUCKY13 (CVE-2013-0169), experimental    potentially VULNERABLE, uses cipher block chaining (CBC) ciphers with TLS
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Okay, so this ISN&amp;rsquo;T the ultimate locked down config. I scrolled down to testssl.sh&amp;rsquo;s cipher suite list to determine which ones were problematic:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Hexcode Cipher Suite Name (OpenSSL)      KeyExch.  Encryption Bits    Cipher Suite Name (RFC)
-----------------------------------------------------------------------------------------------------------------------------
 # ...
 xc028  ECDHE-RSA-AES256-SHA384          ECDH 256  AES        256     TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
 # ...
 xc027  ECDHE-RSA-AES128-SHA256          ECDH 256  AES        128     TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I removed the those ciphers from the list, and the config turned into:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLHonorCipherOrder on
SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And indeed, this got a clean bill of health from testssl.sh, but the number of clients it eliminated was&amp;hellip; a lot:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Android 2.3.7               No connection
Android 4.1.1               No connection
Android 4.3                 No connection
Android 4.4.2               TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Android 5.0.0               TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
Android 6.0                 TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
Android 7.0                 TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Chrome 51 Win 7             TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Chrome 57 Win 7             TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Firefox 49 Win 7            TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Firefox 53 Win 7            TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
IE 6 XP                     No connection
IE 7 Vista                  No connection
IE 8 XP                     No connection
IE 8 Win 7                  No connection
IE 11 Win 7                 No connection
IE 11 Win 8.1               No connection
IE 11 Win Phone 8.1 Update  No connection
IE 11 Win 10                TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Edge 13 Win 10              TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Edge 13 Win Phone 10        TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Opera 17 Win 7              No connection
Safari 5.1.9 OS X 10.6.8    No connection
Safari 7 iOS 7.1            No connection
Safari 9 OS X 10.11         TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Safari 10 OS X 10.12        TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Apple ATS 9 iOS 9           TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Tor 17.0.9 Win 7            No connection
Java 6u45                   No connection
Java 7u25                   No connection
Java 8u31                   TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
OpenSSL 1.0.1l              TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
OpenSSL 1.0.2e              TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So, it seems I found the most locked-down config, and it was time to walk it back until I was comfortable. I definitely want to still support Internet Explorer on Windows 7 and 8, so I glanced back at the results from the current config and added &lt;code&gt;DHE-RSA-AES256-GCM-SHA384&lt;/code&gt; back to the list of supported ciphers. Still strong, still providing forward secrecy:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLHonorCipherOrder on
SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:DHE-RSA-AES256-GCM-SHA384
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And it gave me the following results:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Android 2.3.7               No connection
Android 4.1.1               No connection
Android 4.3                 No connection
Android 4.4.2               TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Android 5.0.0               TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
Android 6.0                 TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
Android 7.0                 TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Chrome 51 Win 7             TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Chrome 57 Win 7             TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Firefox 49 Win 7            TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Firefox 53 Win 7            TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
IE 6 XP                     No connection
IE 7 Vista                  No connection
IE 8 XP                     No connection
IE 8 Win 7                  No connection
IE 11 Win 7                 TLSv1.2 DHE-RSA-AES256-GCM-SHA384, 4096 bit DH
IE 11 Win 8.1               TLSv1.2 DHE-RSA-AES256-GCM-SHA384, 4096 bit DH
IE 11 Win Phone 8.1 Update  TLSv1.2 DHE-RSA-AES256-GCM-SHA384, 4096 bit DH
IE 11 Win 10                TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Edge 13 Win 10              TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Edge 13 Win Phone 10        TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Opera 17 Win 7              No connection
Safari 5.1.9 OS X 10.6.8    No connection
Safari 7 iOS 7.1            No connection
Safari 9 OS X 10.11         TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Safari 10 OS X 10.12        TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Apple ATS 9 iOS 9           TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
Tor 17.0.9 Win 7            No connection
Java 6u45                   No connection
Java 7u25                   No connectionJava 8u31
Java 8u31                   TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256, 256 bit ECDH (P-256)
OpenSSL 1.0.1l              TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
OpenSSL 1.0.2e              TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384, 256 bit ECDH (P-256)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And from SSL Labs:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/nextcloud-cipher-update-ssllabs-after.png&#34; alt=&#34;ssl labs result after update&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;That gives me client coverage I&amp;rsquo;m willing to accept given various &lt;a href=&#34;https://www.w3counter.com/globalstats.php&#34;&gt;usage statistics&lt;/a&gt;.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>ROS 2 launch: required nodes</title>
      <link>https://kyrofa.com/posts/ros-2-launch-required-nodes/</link>
      <pubDate>Mon, 11 Mar 2019 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/ros-2-launch-required-nodes/</guid>
      <description>When using the Robot Operating System (ROS), it&amp;rsquo;s fairly common to want to shut down a launched system if a specific node (or set of nodes) exits. This is pretty easy in ROS1, because launch files support the required attribute on each node. As a result, crafting a two-node system where one of the nodes is required is straightforward:
&amp;lt;launch&amp;gt; &amp;lt;node name = &amp;#34;talker&amp;#34; pkg = &amp;#34;talker&amp;#34; type = &amp;#34;talker_node&amp;#34; /&amp;gt; &amp;lt;node name = &amp;#34;listener&amp;#34; pkg = &amp;#34;listener&amp;#34; type = &amp;#34;talker_node&amp;#34; required = &amp;#34;true&amp;#34; /&amp;gt; &amp;lt;/launch&amp;gt; This launch file creates a talker/listener system where, if the talker exits the system continues trucking along, but if the listener exits the entire launched system is shut down.</description>
      <content:encoded>&lt;p&gt;When using the Robot Operating System (ROS), it&amp;rsquo;s fairly common to want to shut down a launched system if a specific node (or set of nodes) exits. This is pretty easy in ROS1, because launch files support the &lt;code&gt;required&lt;/code&gt; attribute on each node. As a result, crafting a two-node system where one of the nodes is required is straightforward:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;launch&amp;gt;&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;node&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name =&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;talker&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;pkg =&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;talker&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;type =&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;talker_node&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;node&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name =&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;listener&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;pkg =&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;listener&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;type =&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;talker_node&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;required =&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/launch&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This launch file creates a talker/listener system where, if the talker exits the system continues trucking along, but if the &lt;em&gt;listener&lt;/em&gt; exits the entire launched system is shut down.&lt;/p&gt;
&lt;p&gt;In ROS 2 Crystal&amp;rsquo;s &lt;code&gt;launch&lt;/code&gt; system, getting similar functionality involves a lot more boilerplate:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; launch
&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; launch_ros.actions

&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generate_launch_description&lt;/span&gt;():
   talker &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; launch_ros&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;actions&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Node(
       package&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;demo_nodes_py&amp;#34;&lt;/span&gt;,
       node_executable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;talker&amp;#34;&lt;/span&gt;,
       node_name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;talker&amp;#34;&lt;/span&gt;,
       output&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;screen&amp;#34;&lt;/span&gt;)

   listener &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; launch_ros&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;actions&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Node(
       package&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;demo_nodes_py&amp;#34;&lt;/span&gt;,
       node_executable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;listener_qos&amp;#34;&lt;/span&gt;,
       node_name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;listener&amp;#34;&lt;/span&gt;,
       output&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;screen&amp;#34;&lt;/span&gt;,
       arguments&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--number_of_cycles&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;])

   &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; launch&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;LaunchDescription([
       talker,
       listener,
       launch&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;actions&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;RegisterEventHandler(
           event_handler&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;launch&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;event_handlers&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;OnProcessExit(
               target_action&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;listener,
               on_exit&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[
                   launch&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;actions&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;LogInfo(
                       msg&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Listener exited; tearing down entire system.&amp;#34;&lt;/span&gt;),
                   launch&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;actions&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;EmitEvent(event&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;launch&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;events&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Shutdown())]))
   ])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This also creates a talker/listener system where, if the talker exits the system continues, but if the listener exits the launched system is shut down. However, you can see that, unlike the ROS 1 example, there are a few steps required to get there. Specifically, it requires the &lt;code&gt;LaunchDescription&lt;/code&gt; to include an event handler to listen for an exit event for &lt;em&gt;every required node&lt;/em&gt; which then emits a &lt;code&gt;Shutdown&lt;/code&gt; event, which then FINALLY causes the launched system to shut down. That doesn&amp;rsquo;t scale particularly well to a real system where a large number of nodes may be required to run successfully.&lt;/p&gt;
&lt;p&gt;We went through a &lt;a href=&#34;https://github.com/ros2/launch/issues/174&#34;&gt;few design iterations&lt;/a&gt; for how to best solve this, and decided that both the scaling and the boilerplate issues could be solved if the &lt;code&gt;Node&lt;/code&gt; definitions could specify somehow that they were required. Rather than carry over that language from ROS 1, though, we decided to keep some commonality with &lt;code&gt;OnProcessExit&lt;/code&gt; and simply add an &lt;code&gt;on_exit&lt;/code&gt; action list directly to the &lt;code&gt;Node&lt;/code&gt; definition. We also added a new action called &lt;code&gt;Shutdown&lt;/code&gt;. Using these two new features together allows one to very simply specify that, if a given node exits, it should shut the entire launched system down. This greatly reduces boilerplate and scales far better than adding an event handler for each node. Take a look:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; launch
&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; launch_ros.actions

&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;generate_launch_description&lt;/span&gt;():
   &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; launch&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;LaunchDescription([
       launch_ros&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;actions&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Node(
           package&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;demo_nodes_py&amp;#34;&lt;/span&gt;,
           node_executable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;talker&amp;#34;&lt;/span&gt;,
           node_name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;talker&amp;#34;&lt;/span&gt;,
           output&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;screen&amp;#34;&lt;/span&gt;),

       launch_ros&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;actions&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Node(
           package&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;demo_nodes_py&amp;#34;&lt;/span&gt;,
           node_executable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;listener_qos&amp;#34;&lt;/span&gt;,
           node_name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;listener&amp;#34;&lt;/span&gt;,
           output&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;screen&amp;#34;&lt;/span&gt;,
           arguments&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--number_of_cycles&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;],
           on_exit&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;launch&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;actions&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Shutdown())
   ])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This functionality is already available in master, and will of course be included in Dashing (scheduled for June).&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Building ROS 2 snaps with Colcon</title>
      <link>https://kyrofa.com/posts/building-ros-2-snaps-with-colcon/</link>
      <pubDate>Thu, 28 Feb 2019 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/building-ros-2-snaps-with-colcon/</guid>
      <description>The snapcraft CLI has supported building ROS1 snaps for a while via the catkin plugin. We supported the ROS2 betas via the ament plugin, but that was before Open Robotics had a ROS 2 package repository setup, which meant that the ament plugin built the ROS 2 underlay from source, and it was predictably dreadfully slow. However, the stable releases of ROS2 introduced a new build system called colcon, and also had their own package repositories setup.</description>
      <content:encoded>&lt;p&gt;The snapcraft CLI has supported building ROS1 snaps for a while via the &lt;code&gt;catkin&lt;/code&gt; plugin. We supported the ROS2 betas via the &lt;code&gt;ament&lt;/code&gt; plugin, but that was before Open Robotics had a ROS 2 package repository setup, which meant that the &lt;code&gt;ament&lt;/code&gt; plugin built the ROS 2 underlay from source, and it was predictably dreadfully slow. However, the stable releases of ROS2 introduced a new build system called &lt;code&gt;colcon&lt;/code&gt;, and also had their own package repositories setup. Version 3.2 of the snapcraft CLI (just released today) sees the addition of a &lt;code&gt;colcon&lt;/code&gt; plugin to support those new releases, and I&amp;rsquo;d like to give you a quick run-through of its capabilities.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;In order to follow along, make sure you have at least v3.2 of the snapcraft CLI. The best way to do that is to install the snap:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install --classic snapcraft
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Some previous experience &lt;a href=&#34;https://snapcraft.io/&#34;&gt;building a snap&lt;/a&gt; will also be helpful.&lt;/p&gt;
&lt;h2 id=&#34;lets-get-started&#34;&gt;Let&amp;rsquo;s get started&lt;/h2&gt;
&lt;h3 id=&#34;create-the-snapcraftyaml&#34;&gt;Create the &lt;code&gt;snapcraft.yaml&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;First of all, create a new directory and initialize it with a &lt;code&gt;snapcraft.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;~$ mkdir ros2-snap
~$ cd ros2-snap/
~/ros2-snap$ snapcraft init
Created snap/snapcraft.yaml.
Go to https://docs.snapcraft.io/the-snapcraft-format/8337 &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; more information about the snapcraft.yaml format.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Open that &lt;code&gt;snap/snapcraft.yaml&lt;/code&gt; file, and make it look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ros2-talker-listener&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0.1&amp;#34;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;summary&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ROS2 Talker/Listener Example&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;description&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; &lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;This example launches a ROS2 talker and listener.&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;grade&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;devel&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;confinement&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;strict&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;base&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;core18&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;parts&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;ros-demos&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;colcon&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;source&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;https://github.com/ros2/demos.git&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;source-branch&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;crystal&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;colcon-rosdistro&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;crystal&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;colcon-source-space&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;demo_nodes_cpp&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;build-packages&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;make, gcc, g++]&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;stage-packages&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;ros-crystal-ros2launch]&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;apps&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;command&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;opt/ros/crystal/bin/ros2 launch demo_nodes_cpp talker_listener.launch.py&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;plugs&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;network, network-bind]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s break that down and go through it by section.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ros2-talker-listener&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0.1&amp;#34;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;summary&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ROS2 Talker/Listener Example&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;description&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; &lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;This example launches a ROS2 talker and listener.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is the basic metadata required by all snaps. These fields are fairly self-explanatory, but note that the &lt;code&gt;name&lt;/code&gt; must be globally unique among all snaps. You might consider appending your developer name to the end of the snap name, for example.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;grade&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;devel&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;confinement&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;strict&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;base&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;core18&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;grade&lt;/code&gt; can be either &lt;code&gt;stable&lt;/code&gt; or &lt;code&gt;devel&lt;/code&gt;. If it&amp;rsquo;s &lt;code&gt;devel&lt;/code&gt;, the store will prevent you from releasing into one of the two stable channels (&lt;code&gt;stable&lt;/code&gt; and &lt;code&gt;candidate&lt;/code&gt;, specifically). If it&amp;rsquo;s &lt;code&gt;stable&lt;/code&gt;, you can release it anywhere.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;confinement&lt;/code&gt; can be &lt;code&gt;strict&lt;/code&gt;, &lt;code&gt;devmode&lt;/code&gt;, or &lt;code&gt;classic&lt;/code&gt;. &lt;code&gt;strict&lt;/code&gt; enforces confinement, whereas &lt;code&gt;devmode&lt;/code&gt; allows all accesses, even those that would be disallowed under &lt;code&gt;strict&lt;/code&gt; confinement, and logs access that would be disallowed. &lt;code&gt;classic&lt;/code&gt; is even less confined than &lt;code&gt;devmode&lt;/code&gt; in that it doesn&amp;rsquo;t even get private namespaces anymore (among other things). There is &lt;a href=&#34;https://docs.snapcraft.io/snap-confinement/6233&#34;&gt;more extensive documentation&lt;/a&gt; on confinement available.&lt;/p&gt;
&lt;p&gt;As I&amp;rsquo;ve said in the past, I typically use &lt;code&gt;strict&lt;/code&gt; confinement unless I know for sure that the thing I&amp;rsquo;m snapping won&amp;rsquo;t run successfully under confinement, in which case I&amp;rsquo;ll use &lt;code&gt;devmode&lt;/code&gt;. I typically avoid &lt;code&gt;classic&lt;/code&gt; unless I never intend for the snap to run confined (e.g. you&amp;rsquo;ll notice the snapcraft CLI is a classic snap, since it needs more access to the host than confinement would allow).&lt;/p&gt;
&lt;p&gt;Finally, the &lt;code&gt;base&lt;/code&gt; keyword specifies a special kind of snap that provides a minimal set of libraries common to most applications (e.g. libc). It will be the root filesystem for this snap. In this case, we&amp;rsquo;re using &lt;code&gt;core18&lt;/code&gt; which is a minimal rootfs based upon Ubuntu Bionic (18.04).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;parts&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;ros-demos&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;colcon&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;source&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;https://github.com/ros2/demos.git&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;source-branch&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;crystal&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;colcon-rosdistro&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;crystal&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;colcon-source-space&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;demo_nodes_cpp&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;build-packages&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;make, gcc, g++]&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;stage-packages&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;ros-crystal-ros2launch]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The snapcraft CLI is responsible for taking many disparate parts and orchestrating them all into one cohesive snap. You tell it the parts that make up your snap, and it takes care of the rest. Here, we&amp;rsquo;re saying that we have a single part called &lt;code&gt;ros-demos&lt;/code&gt;. We specify that it builds using the &lt;code&gt;colcon&lt;/code&gt; plugin, and we point it to the ROS 2 demos GitHub repository (this could just as easily be a directory on disk). We also specify that we&amp;rsquo;re using the newest ROS 2 release as of this writing: Crystal. We point the &lt;code&gt;colcon&lt;/code&gt; plugin at the C++ demo nodes specifically. We also provide a list of packages that need to be installed in order to build (&lt;code&gt;build-packages&lt;/code&gt;), and also ask that &lt;code&gt;ros-crystal-ros2launch&lt;/code&gt; gets staged into the snap alongside the rest of the part to be used at runtime (specifically, we&amp;rsquo;ll use it in the app, below). To view all the options supported by the &lt;code&gt;colcon&lt;/code&gt; plugin, run the command &lt;code&gt;snapcraft help colcon&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apps&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;command&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;opt/ros/crystal/bin/ros2 launch demo_nodes_cpp talker_listener.launch.py&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;plugs&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;network, network-bind]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This part is interesting. When we build this snap, it will include a complete ROS 2 system: &lt;code&gt;rclcpp&lt;/code&gt;, the &lt;code&gt;demo_nodes_cpp&lt;/code&gt; workspace, etc. It could contain the entire system necessary for a robot in one installable blob. It&amp;rsquo;s a standalone unit: we&amp;rsquo;re in total control of how we want our users to interact with it. We exercise that control via the &lt;code&gt;apps&lt;/code&gt; keyword, where we expose specific commands to the user. Here we specify that this snap has a single app called &lt;code&gt;run&lt;/code&gt;. The command that this app actually runs within the snap uses the &lt;code&gt;ros-crystal-ros2launch&lt;/code&gt; that we staged to fire up the demo nodes&#39; talker/listener launch file. Finally, we use &lt;code&gt;plugs&lt;/code&gt; to specify that this app requires network access (&lt;a href=&#34;https://docs.snapcraft.io/interface-management/6154&#34;&gt;read more about interfaces&lt;/a&gt;).&lt;/p&gt;
&lt;h3 id=&#34;build-the-snap&#34;&gt;Build the snap&lt;/h3&gt;
&lt;p&gt;Now that we&amp;rsquo;ve defined the &lt;code&gt;snapcraft.yaml&lt;/code&gt;, it&amp;rsquo;s time to build the snap. Make sure you&amp;rsquo;re in the directory we created earlier (the one that contained the &lt;code&gt;snap/&lt;/code&gt; directory), and run &lt;code&gt;snapcraft&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;~$ cd ros2-snap/
~/ros2-snap$ snapcraft
&amp;lt;snip&amp;gt;
Snapped ros2-talker-listener_0.1_amd64.snap
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that depending on your host and whether or not you&amp;rsquo;ve built snaps in the past, the snapcraft CLI may prompt you to install Multipass, a tool used by the snapcraft CLI to manage VMs for building snaps.&lt;/p&gt;
&lt;p&gt;The build process will take a few minutes. You&amp;rsquo;ll see the snapcraft CLI fetch &lt;code&gt;rosdep&lt;/code&gt;, which is then used to determine the dependencies of the packages in the &lt;code&gt;colcon&lt;/code&gt; workspace. It then pulls those down and unpacks them into the snap, and finally builds the packages in the workspace and installs them into the snap as well. At the end, you&amp;rsquo;ll have your snap.&lt;/p&gt;
&lt;h3 id=&#34;test-the-snap&#34;&gt;Test the snap&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s install the snap we just built:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;~/ros2-snap$ sudo snap install --dangerous ros2-talker-listener_0.1_amd64.snap
ros2-talker-listener 0.1 installed
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note the use of the &lt;code&gt;--dangerous&lt;/code&gt; flag. That&amp;rsquo;s required because we&amp;rsquo;re installing a snap from disk instead of using the store, and snapd (the daemon with which we&amp;rsquo;re communicating using the &lt;code&gt;snap&lt;/code&gt; command) only trusts snaps that it can cryptographically verify as being from the store unless we tell it otherwise with this flag.&lt;/p&gt;
&lt;p&gt;Finally, let&amp;rsquo;s run the app we defined in the &lt;code&gt;snapcraft.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ ros2-talker-listener.run
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;launch&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: process&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;talker-1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: started with pid &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;26273&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;launch&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: process&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;listener-2&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: started with pid &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;26274&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;talker&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Publishing: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello World: 1&amp;#39;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;listener&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: I heard: &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;Hello World: 1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;talker&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Publishing: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello World: 2&amp;#39;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;listener&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: I heard: &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;Hello World: 2&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, it works great. You could hand this snap to anyone with a snap-capable system, even if they don&amp;rsquo;t have ROS installed, and it would work exactly the same way for them.&lt;/p&gt;
&lt;p&gt;I hope this gives you a decent overview of the capabilities of the &lt;code&gt;colcon&lt;/code&gt; plugin, and I look forward to seeing what you do with it! Please feel free to ask any questions on the &lt;a href=&#34;https://forum.snapcraft.io/&#34;&gt;Snapcraft forums&lt;/a&gt;, or on the &lt;a href=&#34;https://discourse.ros.org/&#34;&gt;ROS forums&lt;/a&gt;. I&amp;rsquo;d love to hear any feedback you have.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Are you passionate about open-source robotics? Join us.</title>
      <link>https://kyrofa.com/posts/are-you-passionate-about-open-source-robotics-join-us/</link>
      <pubDate>Fri, 22 Feb 2019 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/are-you-passionate-about-open-source-robotics-join-us/</guid>
      <description>Ubuntu is a very popular operating system for robotics, for a number of reasons. Perhaps the software stack one happens to use prefers Ubuntu, or the hardware one is using only distributes drivers for Ubuntu. Maybe one&amp;rsquo;s team is already familiar with it, or decision-makers like that it has long-term support releases and extended security maintenance. Perhaps one views snaps as the perfect way to support and update robots in the field, and so on.</description>
      <content:encoded>&lt;p&gt;Ubuntu is a very popular operating system for robotics, for a number of reasons. Perhaps the software stack one happens to use prefers Ubuntu, or the hardware one is using only distributes drivers for Ubuntu. Maybe one&amp;rsquo;s team is already familiar with it, or decision-makers like that it has long-term support releases and extended security maintenance. Perhaps one views snaps as the perfect way to support and update robots in the field, and so on. However, there&amp;rsquo;s a problem with this list: none of these reasons really have anything to do with robotics. That isn&amp;rsquo;t good enough.&lt;/p&gt;
&lt;p&gt;Why is Ubuntu so popular in the cloud? There are a number of reasons (including some of the ones I already provided), but I want to talk about one in particular: because we at Canonical are experts in cloud technology. We have a team working on OpenStack. We have a team working on LXD. People know that if they have a problem, they can turn to us for help and we&amp;rsquo;ll be there. Heck, they can even have us manage their entire cloud for them if they want.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s time for us to do the same with robotics. We have a team with robotics expertise, and we&amp;rsquo;re looking for more talented engineers to join us. The work is challenging and diverse:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Working in upstream &lt;a href=&#34;http://www.ros.org/&#34;&gt;ROS1 and ROS2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Developing infrastructure to properly support ROS1 and ROS2 in Ubuntu&lt;/li&gt;
&lt;li&gt;Designing and developing robotics-related features in snapd and snapcraft&lt;/li&gt;
&lt;li&gt;Writing blog posts, creating videos, and presenting at conferences&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is really one big perk to this gig: you get to work with &lt;strong&gt;me&lt;/strong&gt;. &lt;em&gt;Mic drop&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Oh, that&amp;rsquo;s not enough? Sheesh, fine, you also get to work from home and travel the world, if you&amp;rsquo;re into that kind of thing.&lt;/p&gt;
&lt;p&gt;If this sounds interesting to you, I&amp;rsquo;d love to talk. There are actually two different roles available that tackle slightly different aspects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://boards.greenhouse.io/canonical/jobs/1535166&#34;&gt;Robotics Engineer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://boards.greenhouse.io/canonical/jobs/1550997&#34;&gt;Robotics Security Engineer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The roles are open as long as those links are live!&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Develop your snaps faster</title>
      <link>https://kyrofa.com/posts/develop-your-snaps-faster/</link>
      <pubDate>Thu, 25 Oct 2018 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/develop-your-snaps-faster/</guid>
      <description>Snaps bundle their dependencies, which take time to fetch and unpack, and they&amp;rsquo;re typically made up of multiple parts, each of which need to be built and installed into the snap. Once you finish this process, you need to install the snap and test it out. Once you notice that your code is terrible and doesn&amp;rsquo;t work the way you thought, you have to make a change and go back through that process again.</description>
      <content:encoded>&lt;p&gt;Snaps bundle their dependencies, which take time to fetch and unpack, and they&amp;rsquo;re typically made up of multiple parts, each of which need to be built and installed into the snap. Once you finish this process, you need to install the snap and test it out. Once you notice that your code is terrible and doesn&amp;rsquo;t work the way you thought, you have to make a change and go back through that process again.&lt;/p&gt;
&lt;h2 id=&#34;the-typical-snap-building-experience&#34;&gt;The typical snap-building experience&lt;/h2&gt;
&lt;p&gt;This is the snap development process I see too often:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;snapcraft&lt;/code&gt;, which fetches/builds dependencies and all parts, and creates a snap. This can take quite a chunk of time, depending on the snap.&lt;/li&gt;
&lt;li&gt;Install the snap to test it out.&lt;/li&gt;
&lt;li&gt;Realize it&amp;rsquo;s broken.&lt;/li&gt;
&lt;li&gt;Tweak the code to fix.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;snapcraft&lt;/code&gt; again.&lt;/li&gt;
&lt;li&gt;One of two things happen, depending on the version of snapcraft being used: versions prior to 2.43 change nothing and happily create the exact same broken snap you just tested; later versions error out saying &amp;ldquo;hey, the source code changed.&amp;rdquo; You might find that latter option equally annoying, but it&amp;rsquo;s better, trust me. I&amp;rsquo;ll explain why in a minute.&lt;/li&gt;
&lt;li&gt;As a result of (6), regardless of the outcome, the developer typically runs &lt;code&gt;snapcraft clean&lt;/code&gt; and goes back to step (1).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Stop. There&amp;rsquo;s a better way. Then there&amp;rsquo;s actually an even &lt;em&gt;better&lt;/em&gt; way.&lt;/p&gt;
&lt;h2 id=&#34;the-better-way-careful-cleaning&#34;&gt;The better way: careful cleaning&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s say you just finished step 4. If you run &lt;code&gt;snapcraft clean&lt;/code&gt; right now, you&amp;rsquo;ll remove everything &lt;code&gt;snapcraft&lt;/code&gt; has done: all the dependencies that have been unpacked for every part, all the source code fetched, everything. For the typical project, this shotgun approach is not necessary. Snapcraft supports cleaning individual parts, and even individual steps of individual parts if you want. In this example, you probably want to run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft clean &amp;lt;part you changed&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As another example, say you just wanted to clean the stage step of &amp;ldquo;part1&amp;rdquo;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft clean part1 --step&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;stage
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Using this approach, you only clean exactly what you &lt;em&gt;need&lt;/em&gt; cleaned, and nothing else. All the other parts will remain as they were, and when you run &lt;code&gt;snapcraft&lt;/code&gt; again it&amp;rsquo;ll leave them alone and only update what you cleaned.&lt;/p&gt;
&lt;h2 id=&#34;the-best-way-a-smarter-snapcraft-cli&#34;&gt;The best way: a smarter snapcraft CLI&lt;/h2&gt;
&lt;p&gt;As of &lt;a href=&#34;https://forum.snapcraft.io/t/call-for-testing-snapcraft-3-0/8144&#34;&gt;version 3.0 of the snapcraft CLI&lt;/a&gt;, it&amp;rsquo;s gotten quite a bit smarter (2.43 also contains some of this, but it&amp;rsquo;s better in 3.0). The problem is, implanting the extra brains it needed involved some very significant changes, so we decided to leave it mostly disabled by default for now. The only indication you get that something has changed is that it bails if you change the source of a part. Let me show you how to flip the switch to enable the new behavior (with a caveat: it&amp;rsquo;s new, beware, and please &lt;a href=&#34;https://bugs.launchpad.net/snapcraft/+filebug&#34;&gt;log a bug&lt;/a&gt; if you see something weird).&lt;/p&gt;
&lt;p&gt;Create a new file with the path &lt;code&gt;~/.config/snapcraft/cli.cfg&lt;/code&gt;, and put the following into it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Lifecycle]
outdated_step_action = clean
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That&amp;rsquo;s it. Now your workflow should be more intuitive, like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;snapcraft&lt;/code&gt;, which fetches/builds dependencies and all parts, and creates a snap. This can still take quite a chunk of time, depending on the snap.&lt;/li&gt;
&lt;li&gt;Install the snap to test it out.&lt;/li&gt;
&lt;li&gt;Realize it&amp;rsquo;s broken.&lt;/li&gt;
&lt;li&gt;Tweak the code to fix.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;snapcraft&lt;/code&gt; again.&lt;/li&gt;
&lt;li&gt;Snapcraft should notice that your code changed, and will update that part to incorporate the changes. Return to step (2).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Much better, right? There&amp;rsquo;s one more improvement that can be made to this workflow.&lt;/p&gt;
&lt;h2 id=&#34;stop-installing-your-snap-to-test&#34;&gt;Stop installing your snap to test&lt;/h2&gt;
&lt;p&gt;Snaps are squashfs images, which by definition are read-only. It takes time to create the image, and also requires you to install the snap again every time it changes. Both of these are problems in the development loop: you can&amp;rsquo;t tweak it easily, and needing to create a new one/install it again is time better spent coding. Allow me to introduce you to a feature that has existed for a while but about which few people seem to be aware: &lt;code&gt;snap try&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When you run &lt;code&gt;snapcraft&lt;/code&gt; with no arguments, you&amp;rsquo;re asking it to go through its entire lifecycle of steps and then finally create a snap. Specifically, that&amp;rsquo;s &lt;code&gt;pull&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;stage&lt;/code&gt;, and finally &lt;code&gt;prime&lt;/code&gt;. In the prime step, the snapcraft CLI creates a directory named &lt;code&gt;prime/&lt;/code&gt; and puts everything that makes up the final snap in there: the proper metadata that defines it as a valid snap, etc. The process of turning that into a snap is literally creating a squashfs image of the &lt;code&gt;prime/&lt;/code&gt; directory. If you then unsquashed that image, you&amp;rsquo;d end up with exactly the contents of the &lt;code&gt;prime/&lt;/code&gt; directory again. &lt;code&gt;snap try&lt;/code&gt; is snapd&amp;rsquo;s way of supporting taking a snap for a spin &lt;em&gt;from a directory&lt;/em&gt; instead of from a squashfs image. It essentially gives you a read/write snap, which changes your workflow again. Now it looks like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;snapcraft prime&lt;/code&gt;, which fetches/builds dependencies and all parts, and creates all the metadata that turns it into a valid snap in the &lt;code&gt;prime/&lt;/code&gt; directory, but &lt;em&gt;doesn&amp;rsquo;t&lt;/em&gt; create a snap out of it.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;snap try prime/&lt;/code&gt; to test out the snap defined in that directory.&lt;/li&gt;
&lt;li&gt;Realize it&amp;rsquo;s broken.&lt;/li&gt;
&lt;li&gt;Tweak the code to fix.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;snapcraft prime&lt;/code&gt; again to incorporate your changes into the &lt;code&gt;prime/&lt;/code&gt; directory.&lt;/li&gt;
&lt;li&gt;Test the snap out again (your changes are already available for use since they&amp;rsquo;re in the prime directory, no need to install or try it again). Return to step (3).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That&amp;rsquo;s a pretty nice workflow. You could even skip step 4 and 5 if you really just want to hack in the &lt;code&gt;prime/&lt;/code&gt; directory; your changes will immediately be reflected in the snap being tried.&lt;/p&gt;
&lt;p&gt;I hope this proves useful to you. As always, if you have any questions, ask away in the &lt;a href=&#34;https://forum.snapcraft.io/&#34;&gt;Snapcraft Forum&lt;/a&gt;. Now go forth and develop snaps faster.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>ROS Melodic on Fedora 27</title>
      <link>https://kyrofa.com/posts/ros-melodic-on-fedora-27/</link>
      <pubDate>Fri, 25 May 2018 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/ros-melodic-on-fedora-27/</guid>
      <description>Part of the snapcraft CLI&amp;rsquo;s development roadmap this cycle includes the ability to build snaps on distros other than Ubuntu. Part of that work includes investigating each plugin to determine if it&amp;rsquo;s possible to get it working on other distros. As a result, I&amp;rsquo;ve been looking into the Catkin plugin recently, using Fedora 27 as my proving ground (since that&amp;rsquo;s the distro with which I&amp;rsquo;m most familiar after Ubuntu).
The first thing you&amp;rsquo;ll notice trying to get ROS working on Fedora is that, unlike Ubuntu, where Open Robotics maintains infrastructure hosting a repository of Debian packages, there is no repository for you to use: you must build ROS from source.</description>
      <content:encoded>&lt;p&gt;Part of the &lt;a href=&#34;https://forum.snapcraft.io/t/snapcraft-cli-18-04-roadmap/5574&#34;&gt;snapcraft CLI&amp;rsquo;s development roadmap&lt;/a&gt; this cycle includes the ability to build snaps on distros other than Ubuntu. Part of that work includes investigating each plugin to determine if it&amp;rsquo;s possible to get it working on other distros. As a result, I&amp;rsquo;ve been looking into the &lt;a href=&#34;https://docs.snapcraft.io/build-snaps/ros&#34;&gt;Catkin&lt;/a&gt; plugin recently, using Fedora 27 as my proving ground (since that&amp;rsquo;s the distro with which I&amp;rsquo;m &lt;a href=&#34;https://bodhi.fedoraproject.org/users/kyrofa&#34;&gt;most familiar&lt;/a&gt; after Ubuntu).&lt;/p&gt;
&lt;p&gt;The first thing you&amp;rsquo;ll notice trying to get ROS working on Fedora is that, unlike Ubuntu, where Open Robotics maintains infrastructure hosting a repository of Debian packages, there is no repository for you to use: you must build ROS from source. There&amp;rsquo;s some decent documentation for how to do this, but it&amp;rsquo;s severely lacking for Fedora, so I thought I&amp;rsquo;d share how I got it working.&lt;/p&gt;
&lt;p&gt;The biggest issue I hit is that &lt;code&gt;rosdep&lt;/code&gt; is completely useless for the dependencies of ROS itself. Honestly, I&amp;rsquo;m not sure why, a quick peek at the rules shows that it definitely supports Fedora. Maybe just none of the dependencies for ROS have Fedora 27 rules; I didn&amp;rsquo;t investigate.&lt;/p&gt;
&lt;p&gt;So instead of using &lt;code&gt;rosdep&lt;/code&gt;, I figured out the dependencies using good old trial and error. If you don&amp;rsquo;t feel like doing the same, you can borrow the ones I found (although I was only playing with &lt;code&gt;ros_comm&lt;/code&gt;, no GUI tools):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# dnf install python-rosdep python-rosinstall_generator \&lt;/span&gt;
              python-rosinstall python-pyparsing &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;              cmake gcc-c++ python-empy boost-devel console-bridge-devel &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;              poco-devel python-devel tinyxml2-devel lz4-devel gtest-devel &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;              bzip2-devel gpgme-devel python-defusedxml python-netifaces
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now create a workspace for ROS:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ mkdir ~/ros_catkin_ws
$ cd ~/ros_catkin_ws
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Fetch the source for Melodic:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ rosinstall_generator-2.7 ros_comm --rosdistro melodic --deps --tar &amp;gt; melodic-ros_comm.rosinstall
$ wstool init -j8 src melodic-ros_comm.rosinstall
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now build ROS:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ ./src/catkin/bin/catkin_make_isolated --install -DCMAKE_BUILD_TYPE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;Release
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, you can source the &lt;code&gt;setup.bash&lt;/code&gt; for the workspace as usual:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ source ~/ros_catkin_ws/install_isolated/setup.bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you can run e.g. &lt;code&gt;roscore&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ roscore
... logging to /root/.ros/log/35&amp;lt;snip&amp;gt;85d360/roslaunch-fedora-ros-19796.log
Checking log directory &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; disk usage. This may take awhile.
Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is &amp;lt;1GB.
started roslaunch server http://fedora-ros:34509/
ros_comm version 1.14.1

SUMMARY
&lt;span style=&#34;color:#f92672&#34;&gt;========&lt;/span&gt;

PARAMETERS
* /rosdistro: melodic
* /rosversion: 1.14.1

NODES

auto-starting new master
rocess&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;master&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: started with pid &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;19806&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;

ROS_MASTER_URI&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;http://fedora-ros:11311/
setting /run_id to 35d993ce-6062-11e8-bcab-00163e85d360
process&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;rosout-1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: started with pid &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;19817&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
started core service &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;/rosout&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Anyway, just wanted to get that out there for posterity, and in case it helps anyone else.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Your first robot: Sharing with others [5/5]</title>
      <link>https://kyrofa.com/posts/your-first-robot-sharing-with-others-5-5/</link>
      <pubDate>Fri, 16 Mar 2018 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/your-first-robot-sharing-with-others-5-5/</guid>
      <description>UPDATE: I&amp;rsquo;m leaving this series up for historical purposes, but please note that I no longer recommend Ubuntu Core or snaps for use in robotics.
This is the fifth (and final) blog post in this series about creating your first robot with ROS and Ubuntu Core. In the previous post we discussed methods of control, did a little math, and wrote the ROS driver for our robot. But it still required several nodes to be running at once, and sharing it with the world involved uploading your source code somewhere and convincing people to install ROS, build your package, and use it.</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;UPDATE: I&amp;rsquo;m leaving this series up for historical purposes, but please note that I no longer recommend Ubuntu Core or snaps for use in robotics.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is the fifth (and final) blog post in &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-a-beginner-s-guide-to-ros-and-ubuntu-core-1-5&#34;&gt;this series about creating your first robot with ROS and Ubuntu Core&lt;/a&gt;. In the &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-the-driver-4-5&#34;&gt;previous post&lt;/a&gt; we discussed methods of control, did a little math, and wrote the ROS driver for our robot. But it still required several nodes to be running at once, and sharing it with the world involved uploading your source code somewhere and convincing people to install ROS, build your package, and use it. Today we&amp;rsquo;re going to simplify both of those issues by discussing ROS launch files, and packaging everything we&amp;rsquo;ve written as a snap that can be installed by your friends with a few keystrokes, even without knowing anything about ROS.&lt;/p&gt;
&lt;p&gt;Alright, let&amp;rsquo;s get started! Remember that this is also a video series, feel free to watch the video version of this post:&lt;/p&gt;

&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
  &lt;iframe src=&#34;https://www.youtube-nocookie.com/embed/22tWldE_T7Q&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; allowfullscreen title=&#34;YouTube Video&#34;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h1 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h1&gt;
&lt;p&gt;This post will assume the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You know what snaps are, and have completed the &amp;ldquo;&lt;a href=&#34;https://tutorials.ubuntu.com/tutorial/create-your-first-snap#0&#34;&gt;create your first snap&lt;/a&gt;&amp;rdquo; tutorial.&lt;/li&gt;
&lt;li&gt;You have a store account at &lt;a href=&#34;https://dashboard.snapcraft.io/&#34;&gt;dashboard.snapcraft.io&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;step-1-create-launch-files&#34;&gt;Step 1: Create launch files&lt;/h2&gt;
&lt;p&gt;In parts &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-the-controller-3-5&#34;&gt;3&lt;/a&gt; and &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-the-driver-4-5&#34;&gt;4&lt;/a&gt;, we needed to open several terminals because we needed to run several different ROS nodes. It&amp;rsquo;s difficult to remember every component of our system, and it gets exponentially harder as we start adding parameters, etc. Running individual nodes is handy for testing things out, but we&amp;rsquo;re done with that step. It&amp;rsquo;s time to make our system a little more of a product, something you can just run and use to control your robot. As you learned in the &lt;a href=&#34;http://wiki.ros.org/ROS/Tutorials/UsingRqtconsoleRoslaunch&#34;&gt;eighth ROS tutorial&lt;/a&gt;, ROS has a tool for dealing with this: launch files. These give us a simple language we can use to specify what nodes need to launch at the same time, and with what parameters.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s get started writing launch files for the ROS system that controls our robot. We&amp;rsquo;ll put these launch files in the &lt;code&gt;edukit_bot&lt;/code&gt; package we&amp;rsquo;ve been working on throughout this series, in a new &lt;code&gt;launch/&lt;/code&gt; folder. Make that folder now:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ mkdir ~/edukit_bot_ws/src/edukit_bot/launch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In order for our robot (and launch files) to be reusable, we&amp;rsquo;re not actually going to create a single launch file. We want a single entry point (i.e. we want to be able to launch a single launch file and have it bring up the whole system), but we also want to keep things as modular as possible. So we&amp;rsquo;ll write one launch file per subsystem, and then one super launch file in charge of bringing up the entire system. We have two subsystems for our robot: low-level control, and teleoperation. Let&amp;rsquo;s write our &amp;ldquo;super&amp;rdquo; launch file first.&lt;/p&gt;
&lt;h3 id=&#34;step-11-write-robot-launch-file&#34;&gt;Step 1.1: Write robot launch file&lt;/h3&gt;
&lt;p&gt;Create a new file &lt;code&gt;~/edukit_bot_ws/src/edukit_bot/launch/edukit_bot.launch&lt;/code&gt; and open it up in your editor. Make it look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;launch&amp;gt;&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;include&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;file=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$(find edukit_bot)/launch/includes/teleop.launch.xml&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;include&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;file=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$(find edukit_bot)/launch/includes/control.launch.xml&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/launch&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. All this launch file does is &amp;ldquo;include&amp;rdquo; the launch files for our two subsystems (which we have yet to write). When this launch file is run, it runs those. Alright, let&amp;rsquo;s get started on our subsystems&#39; launch files.&lt;/p&gt;
&lt;h3 id=&#34;step-12-write-teleop-launch-file&#34;&gt;Step 1.2: Write teleop launch file&lt;/h3&gt;
&lt;p&gt;Why are we splitting these up, exactly? Let&amp;rsquo;s say in the future you want to work toward making this robot autonomous. In that case, you may no longer want the teleoperation nodes to be launched. Do you think you&amp;rsquo;ll remember which nodes relate to teleoperation and which don&amp;rsquo;t? I certainly won&amp;rsquo;t! It&amp;rsquo;s easier just to keep related pieces together, and remove the &amp;ldquo;include&amp;rdquo; line that launches the teleoperation nodes if you want to remove that functionality.&lt;/p&gt;
&lt;p&gt;First, you need to create the &lt;code&gt;includes&lt;/code&gt; directory into which we&amp;rsquo;ll place our subsystem launch files:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ mkdir ~/edukit_bot_ws/src/edukit_bot/launch/includes
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Create a new file &lt;code&gt;~/edukit_bot_ws/src/edukit_bot/launch/includes/teleop.launch.xml&lt;/code&gt;. Why do we end this one with &lt;code&gt;.xml&lt;/code&gt; and not just &lt;code&gt;.launch&lt;/code&gt; like the other one? Because &lt;code&gt;roslaunch&lt;/code&gt; (the command that runs launch files) will tab-complete files that end in &lt;code&gt;.launch&lt;/code&gt;. Since these subsystem launch files don&amp;rsquo;t do much by themselves, we don&amp;rsquo;t want them to be exposed to end users. Ending them with &lt;code&gt;.xml&lt;/code&gt; essentially hides them from &lt;code&gt;roslaunch&lt;/code&gt;, but still lets us use them with includes. Anyway, open that file in your editor. Make it look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;launch&amp;gt;&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;node&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;pkg=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;joy&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;type=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;joy_node&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;joystick&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;param&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;autorepeat_rate&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/node&amp;gt;&lt;/span&gt;

 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;node&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;pkg=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;teleop_twist_joy&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;type=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;teleop_node&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;joystick_to_twist&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;param&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;scale_angular&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;4&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/node&amp;gt;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/launch&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This file launches the two nodes that make up our teleoperation (i.e. what we discussed in &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-the-controller-3-5&#34;&gt;part 3&lt;/a&gt;). Note that we&amp;rsquo;re using the &lt;code&gt;autorepeat_rate&lt;/code&gt; parameter of the joy node. This parameter tells joy to rebroadcast joystick values, even if they haven&amp;rsquo;t changed, at a rate of at least 1 Hz (if the joystick moves, the rate will be much greater than 1 Hz, we just want to make sure we don&amp;rsquo;t run into our driver&amp;rsquo;s timeout functionality if the joystick simply hasn&amp;rsquo;t moved).&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re also using the &lt;code&gt;scale_angular&lt;/code&gt; parameter of the &lt;code&gt;teleop_node&lt;/code&gt; so the robot actually turns (as we discussed in &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-the-driver-4-5&#34;&gt;part 4&lt;/a&gt;).&lt;/p&gt;
&lt;h3 id=&#34;step-13-update-dependencies&#34;&gt;Step 1.3: Update dependencies&lt;/h3&gt;
&lt;p&gt;We&amp;rsquo;ve just finished writing a launch file in our package that assumes the presence of other ROS packages (and their nodes). However, we&amp;rsquo;re not depending on those ROS nodes at all. Let&amp;rsquo;s add both &lt;code&gt;joy&lt;/code&gt; and &lt;code&gt;teleop_twist_joy&lt;/code&gt; to our list of dependencies in our &lt;code&gt;edukit_bot&lt;/code&gt;&amp;rsquo;s &lt;code&gt;package.xml&lt;/code&gt;, making it look something like this in the end:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34;?&amp;gt;&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;format=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;edukit_bot&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;0.1.0&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;description&amp;gt;&lt;/span&gt;The edukit_bot package&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;

   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;maintainer&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;email=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;you@you.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;You&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/maintainer&amp;gt;&lt;/span&gt;

   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;license&amp;gt;&lt;/span&gt;GPLv3&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/license&amp;gt;&lt;/span&gt;

   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;buildtool_depend&amp;gt;&lt;/span&gt;catkin&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/buildtool_depend&amp;gt;&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;build_depend&amp;gt;&lt;/span&gt;rospy&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/build_depend&amp;gt;&lt;/span&gt;

   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;exec_depend&amp;gt;&lt;/span&gt;rospy&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/exec_depend&amp;gt;&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;exec_depend&amp;gt;&lt;/span&gt;geometry_msgs&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/exec_depend&amp;gt;&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;exec_depend&amp;gt;&lt;/span&gt;python-rpi.gpio&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/exec_depend&amp;gt;&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;exec_depend&amp;gt;&lt;/span&gt;joy&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/exec_depend&amp;gt;&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;exec_depend&amp;gt;&lt;/span&gt;teleop_twist_joy&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/exec_depend&amp;gt;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/package&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;step-14-write-the-low-level-control-launch-file&#34;&gt;Step 1.4: Write the low-level control launch file&lt;/h3&gt;
&lt;p&gt;Create a new file &lt;code&gt;~/edukit_bot_ws/src/edukit_bot/launch/includes/control.launch.xml&lt;/code&gt;. Make it look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;launch&amp;gt;&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;node&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;driver&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;pkg=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;edukit_bot&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;type=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;driver_node&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/launch&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This one is tremendously simple, as it&amp;rsquo;s just launching the driver we wrote in &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-the-driver-4-5&#34;&gt;part 4&lt;/a&gt;. No parameters to tweak, since the default values are suitable (at least for my robot, perhaps yours is different).&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re done making changes to our package, so let&amp;rsquo;s rebuild it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/edukit_bot_ws
$ catkin_make
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;step-15-test-launch-system&#34;&gt;Step 1.5: Test launch system&lt;/h3&gt;
&lt;p&gt;Alright, let&amp;rsquo;s take our new launch files for a spin. This time, you only need a single terminal! Make sure it&amp;rsquo;s in your classic shell. First, as in the rest of the series, we need to make sure we have permission to access GPIO as a user (remember this resets upon reboot):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo chmod a+rw /dev/gpiomem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now activate your workspace:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/edukit_bot_ws
$ source devel/setup.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally launch the super launch file, which will launch everything else:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ roslaunch edukit_bot edukit_bot.launch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;ll see all the nodes that make up our system come up, and you should be able to control your robot. So much easier than running them all individually!&lt;/p&gt;
&lt;h2 id=&#34;step-2-package-this-workspace-as-a-snap&#34;&gt;Step 2: Package this workspace as a snap&lt;/h2&gt;
&lt;p&gt;At this point, we have our entire ROS system launchable by a single command. Wouldn&amp;rsquo;t it be neat to have this system running as soon as we boot our Raspberry Pi? Also, it would be cool to ask your friends to take your version of this series for a spin, and be able to test theirs out as well! Both of these are easily accomplished by packaging our ROS system as a snap. Note that the workspace (and &lt;code&gt;snapcraft.yaml&lt;/code&gt;) used here is &lt;a href=&#34;https://github.com/kyrofa/edukit_bot&#34;&gt;available for reference&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;step-21-add-install-rules&#34;&gt;Step 2.1: Add install rules&lt;/h3&gt;
&lt;p&gt;The first step toward packaging anything is typically to ensure that we&amp;rsquo;re installing the components of our package correctly. With its use of workspaces, ROS makes this dangerously easy to ignore, but it&amp;rsquo;s still important. We have two components of our &lt;code&gt;edukit_bot&lt;/code&gt; package: the driver, and the launch files. Open up &lt;code&gt;edukit_bot&lt;/code&gt;&amp;rsquo;s &lt;code&gt;CMakeLists.txt&lt;/code&gt;, and add the following two install rules to the bottom:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-cmake&#34; data-lang=&#34;cmake&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# ... &amp;lt;snip&amp;gt;
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Install driver node
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;install(&lt;span style=&#34;color:#e6db74&#34;&gt;PROGRAMS&lt;/span&gt;
 &lt;span style=&#34;color:#e6db74&#34;&gt;src/driver_node&lt;/span&gt;
 &lt;span style=&#34;color:#e6db74&#34;&gt;DESTINATION&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;${&lt;/span&gt;CATKIN_PACKAGE_BIN_DESTINATION&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
)&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Install launch files
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;install(&lt;span style=&#34;color:#e6db74&#34;&gt;DIRECTORY&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;launch&lt;/span&gt;
 &lt;span style=&#34;color:#e6db74&#34;&gt;DESTINATION&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;${&lt;/span&gt;CATKIN_PACKAGE_SHARE_DESTINATION&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
)&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now when we install our ROS package, it will install both our driver node as well as the launch files we&amp;rsquo;ve written.&lt;/p&gt;
&lt;h3 id=&#34;step-22-install-snapcraft&#34;&gt;Step 2.2: Install Snapcraft&lt;/h3&gt;
&lt;p&gt;In order to build a snap, we need to install a tool called &lt;code&gt;snapcraft&lt;/code&gt;. From your classic shell:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt install snapcraft
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;step-23-write-the-snapcraftyaml&#34;&gt;Step 2.3: Write the &lt;code&gt;snapcraft.yaml&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;We tell &lt;code&gt;snapcraft&lt;/code&gt; how to create our snap by writing a &lt;code&gt;snapcraft.yaml&lt;/code&gt;. We can get an initial version with &lt;code&gt;snapcraft init&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/edukit_bot_ws
$ snapcraft init
Created snap/snapcraft.yaml.
Edit the file to your liking or run &lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;snapcraft&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt; to get started
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Do as it asks, and open up &lt;code&gt;snap/snapcraft.yaml&lt;/code&gt; in your editor. Make it look something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;edukit-bot-kyrofa&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1.0&amp;#39;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;summary&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ROS system for teleoperating edukit bot&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;description&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; A ROS system for teleoperating the EduKit #3, the robotics kit.
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; This accompanies the &amp;#34;Your First Robot&amp;#34; series written by Kyle
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; Fazzari.
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; Note that the `joystick` interface is needed for access to the
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; gamepad, and the `physical-memory-control` interface is needed
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; for access to GPIO. Both of these interfaces must be connected
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; before this snap will function:
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;   $ sudo snap connect edukit-bot-kyrofa:joystick
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;   $ sudo snap connect edukit-bot-kyrofa:physical-memory-control&lt;/span&gt; 

&lt;span style=&#34;color:#f92672&#34;&gt;grade&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;stable&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;confinement&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;strict&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;parts&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;workspace&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;catkin&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;rosdistro&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;kinetic&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;source&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;.&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;catkin-packages&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;edukit_bot]&lt;/span&gt;

   &lt;span style=&#34;color:#75715e&#34;&gt;# Required to build RPi.GPIO&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;build-packages&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;python-dev, python-setuptools]&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;apps&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;launch&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;command&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;roslaunch edukit_bot edukit_bot.launch&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;daemon&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;simple&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;plugs&lt;/span&gt;:
     &lt;span style=&#34;color:#75715e&#34;&gt;# Basic networking&lt;/span&gt;
     - &lt;span style=&#34;color:#ae81ff&#34;&gt;network&lt;/span&gt;
     - &lt;span style=&#34;color:#ae81ff&#34;&gt;network-bind&lt;/span&gt;

     &lt;span style=&#34;color:#75715e&#34;&gt;# Needed for access to gamepad&lt;/span&gt;
     - &lt;span style=&#34;color:#ae81ff&#34;&gt;joystick&lt;/span&gt;

     &lt;span style=&#34;color:#75715e&#34;&gt;# Needed for GPIO memory-mapping&lt;/span&gt;
     - &lt;span style=&#34;color:#ae81ff&#34;&gt;physical-memory-control&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s break this down piece by piece.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;edukit-bot-kyrofa&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1.0&amp;#39;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;summary&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ROS system for teleoperating edukit bot&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;description&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; A ROS system for teleoperating the EduKit #3, the robotics kit.
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; This accompanies the &amp;#34;Your First Robot&amp;#34; series written by Kyle
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; Fazzari.
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; Note that the `joystick` interface is needed for access to the
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; gamepad, and the `physical-memory-control` interface is needed
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; for access to GPIO. Both of these interfaces must be connected
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; before this snap will function:
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;   $ sudo snap connect edukit-bot-kyrofa:joystick
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;   $ sudo snap connect edukit-bot-kyrofa:physical-memory-control&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is just useful metadata for our snap. The name must be unique (that&amp;rsquo;s why I put my nickname at the end), but you&amp;rsquo;re pretty much free to put whatever you want for the rest. The description here hints at confinement, which we&amp;rsquo;ll cover in a moment.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;grade&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;stable&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;confinement&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;strict&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;grade&lt;/code&gt; can be either &lt;code&gt;stable&lt;/code&gt; or &lt;code&gt;devel&lt;/code&gt;. If it&amp;rsquo;s &lt;code&gt;devel&lt;/code&gt;, the store will prevent you from releasing into stable channels&amp;ndash; think of it as a safety net to prevent accidental releases. If it&amp;rsquo;s &lt;code&gt;stable&lt;/code&gt;, you can release it anywhere. I personally think this feature is a tad silly, and typically just set it to &lt;code&gt;stable&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;confinement&lt;/code&gt; can be &lt;code&gt;strict&lt;/code&gt;, &lt;code&gt;devmode&lt;/code&gt;, or &lt;code&gt;classic&lt;/code&gt;. &lt;code&gt;strict&lt;/code&gt; enforces confinement, whereas &lt;code&gt;devmode&lt;/code&gt; allows all accesses, even those that would be disallowed under strict confinement (and logs accesses that would otherwise be disallowed for your reference). &lt;code&gt;classic&lt;/code&gt; is even less confined than &lt;code&gt;devmode&lt;/code&gt;, essentially running exactly like something you&amp;rsquo;d install from a deb. There is &lt;a href=&#34;https://snapcraft.io/docs/reference/confinement&#34;&gt;more extensive documentation on confinement available&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I personally always use &lt;code&gt;strict&lt;/code&gt; confinement unless I know for sure that the thing I&amp;rsquo;m snapping won&amp;rsquo;t run successfully under confinement, in which case I&amp;rsquo;ll use &lt;code&gt;devmode&lt;/code&gt;. I typically avoid &lt;code&gt;classic&lt;/code&gt; unless I never intend for the app to run confined (e.g. if I was snapping vim, I know it would always need access to everything). In this case, this snap can run quite well under &lt;code&gt;strict&lt;/code&gt; confinement. We&amp;rsquo;ll discuss this some more momentarily.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;parts&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;workspace&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;catkin&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;rosdistro&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;kinetic&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;source&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;.&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;catkin-packages&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;edukit_bot]&lt;/span&gt;

   &lt;span style=&#34;color:#75715e&#34;&gt;# Required to build RPi.GPIO&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;build-packages&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;python-dev, python-setuptools]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is where we tell &lt;code&gt;snapcraft&lt;/code&gt; how to build our Catkin workspace. Every snap is made up of a number of parts. In our case, there&amp;rsquo;s only one, called &lt;code&gt;workspace&lt;/code&gt;. We say that it&amp;rsquo;s built using the &lt;code&gt;catkin&lt;/code&gt; plugin, using &lt;code&gt;kinetic&lt;/code&gt; (as opposed to Lunar or Indigo, as we discussed in &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-introduction-to-the-robot-operating-system-2-5&#34;&gt;part 2&lt;/a&gt;). We give it a list of Catkin packages to build, in this case just &lt;code&gt;edukit_bot&lt;/code&gt;. Finally, we give it a list of packages that need to be installed in order to build our ROS package, or more specifically, our &lt;code&gt;RPi.GPIO&lt;/code&gt; dependency.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apps&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;launch&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;command&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;roslaunch edukit_bot edukit_bot.launch&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;daemon&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;simple&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;plugs&lt;/span&gt;:
     &lt;span style=&#34;color:#75715e&#34;&gt;# Basic networking&lt;/span&gt;
     - &lt;span style=&#34;color:#ae81ff&#34;&gt;network&lt;/span&gt;
     - &lt;span style=&#34;color:#ae81ff&#34;&gt;network-bind&lt;/span&gt;

     &lt;span style=&#34;color:#75715e&#34;&gt;# Needed for access to gamepad&lt;/span&gt;
     - &lt;span style=&#34;color:#ae81ff&#34;&gt;joystick&lt;/span&gt;

     &lt;span style=&#34;color:#75715e&#34;&gt;# Needed for GPIO memory-mapping&lt;/span&gt;
     - &lt;span style=&#34;color:#ae81ff&#34;&gt;physical-memory-control&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we expose our single launch file that brings up the whole system. We create a new app called &amp;ldquo;launch&amp;rdquo; that runs the &lt;code&gt;roslaunch&lt;/code&gt; command we used in step 4. We&amp;rsquo;re saying that this should be running at boot with &lt;code&gt;daemon: simple&lt;/code&gt;. Finally, we provide a list of plugs, which is where confinement comes into play. (&lt;strong&gt;Note:&lt;/strong&gt; if you&amp;rsquo;re using Ubuntu Core 18, you should use &lt;code&gt;gpio-memory-control&lt;/code&gt; instead of &lt;code&gt;physical-memory-control&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Since this snap is strictly confined, by default it doesn&amp;rsquo;t really have access to anything. No network, no GPIO, no controller. So we add plugs for each of those things: ROS needs the network for its nodes to function, we need access to the controller, and we need access to GPIO. &lt;a href=&#34;https://docs.snapcraft.io/core/interfaces&#34;&gt;Read more about interfaces&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it! Let&amp;rsquo;s build it.&lt;/p&gt;
&lt;h3 id=&#34;step-24-build-the-snap&#34;&gt;Step 2.4: Build the snap&lt;/h3&gt;
&lt;p&gt;This part is pretty easy:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/edukit_bot_ws
$ snapcraft
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that the Raspberry Pi is not as blazingly fast as we&amp;rsquo;d all like it to be. This will take some time. You&amp;rsquo;ll see &lt;code&gt;snapcraft&lt;/code&gt; fetch &lt;code&gt;rosdep&lt;/code&gt;, which is then used to determine the dependencies of the ROS packages in the workspace. This is only &lt;code&gt;edukit_bot&lt;/code&gt; in our case, which we know depends upon &lt;code&gt;rospy&lt;/code&gt;, &lt;code&gt;geometry_msgs&lt;/code&gt;, &lt;code&gt;python-rpi.gpio&lt;/code&gt;, &lt;code&gt;joy&lt;/code&gt;, and &lt;code&gt;teleop_twist_joy&lt;/code&gt;. It then pulls those down and puts them into the snap along with &lt;code&gt;roscore&lt;/code&gt;. Finally, it builds the requested packages in the workspace, and installs them into the snap as well. At the end, you&amp;rsquo;ll have your snap.&lt;/p&gt;
&lt;h3 id=&#34;step-25-test-your-snap&#34;&gt;Step 2.5: Test your snap&lt;/h3&gt;
&lt;p&gt;Now that we have our snap built, let&amp;rsquo;s test it out! First, install it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/edukit_bot_ws
$ sudo snap install edukit-bot-kyrofa_1.0_armhf.snap --dangerous
edukit-bot-kyrofa 0.1 installed
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that the snap is installed, since we made our ROS system a daemon it&amp;rsquo;s already up and running. Unfortunately, it has already probably died because it can&amp;rsquo;t access the joystick or GPIO. We need to give it permission to do that by connecting the &lt;code&gt;joystick&lt;/code&gt; and &lt;code&gt;physical-memory-control&lt;/code&gt; interfaces:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap connect edukit-bot-kyrofa:joystick
$ sudo snap connect edukit-bot-kyrofa:physical-memory-control
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We only need to do that once. Now that access has been granted, we just need to restart the service so it tries again:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap restart edukit-bot-kyrofa
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you should be able to drive your robot around as before, but everything is running out of that single snap.&lt;/p&gt;
&lt;h2 id=&#34;step-3-share-the-snap-with-others&#34;&gt;Step 3: Share the snap with others&lt;/h2&gt;
&lt;p&gt;All you need to do to share your project with others is release the snap in the store!&lt;/p&gt;
&lt;h3 id=&#34;step-31-tell-snapcraft-who-you-are&#34;&gt;Step 3.1: Tell Snapcraft who you are&lt;/h3&gt;
&lt;p&gt;We&amp;rsquo;re about to use &lt;code&gt;snapcraft&lt;/code&gt; to register and upload a snap using the store account you created when satisfying the prerequisites. For that to work, you need to sign in with &lt;code&gt;snapcraft&lt;/code&gt;. From your classic shell:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft login
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;step-32-register-the-snap-name&#34;&gt;Step 3.2: Register the snap name&lt;/h3&gt;
&lt;p&gt;As I mentioned before, snap names are globally unique, so only one developer can register and publish a snap with a given name. Before you can publish this snap you need to make sure that snap name is registered to you (note that this corresponds to the &lt;code&gt;name&lt;/code&gt; field in the &lt;code&gt;snapcraft.yaml&lt;/code&gt; we created moments ago). Again from your classic shell:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft register &amp;lt;my snap name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Assuming that name is available, you can proceed to upload it. Otherwise you may need to rename/rebuild your snap to whatever name you managed to register (i.e. change the name field in the YAML and run &lt;code&gt;snapcraft&lt;/code&gt; again, it won&amp;rsquo;t take long).&lt;/p&gt;
&lt;h3 id=&#34;step-33-release-the-snap&#34;&gt;Step 3.3: Release the snap&lt;/h3&gt;
&lt;p&gt;There are four release channels available by default. In order of increasing stability, these channels are &lt;code&gt;edge&lt;/code&gt;, &lt;code&gt;beta&lt;/code&gt;, &lt;code&gt;candidate&lt;/code&gt;, and &lt;code&gt;stable&lt;/code&gt;. We&amp;rsquo;ve tested this snap and it&amp;rsquo;s strictly confined, so we can really release it wherever we want. Let&amp;rsquo;s go ahead and put it into the &lt;code&gt;stable&lt;/code&gt; channel:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft push path/to/my.snap --release&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;stable
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once the upload and automated reviews finish successfully, anyone in the world can install your snap on the Raspberry Pi controlling their EduKit bot by running:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install &amp;lt;my snap name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Remember they also need to connect those interfaces so the snap can use the gamepad and GPIO. Mine (&lt;a href=&#34;https://github.com/kyrofa/edukit_bot&#34;&gt;see the code&lt;/a&gt;) is also in the stable channel, so you can take it for a spin with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install edukit-bot-kyrofa
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;That&amp;rsquo;s all, folks! I hope you enjoyed this series, and I hope that this brief introduction to professional robotics will serve you well in the years to come. If this is a field that you&amp;rsquo;d like to pursue, I can&amp;rsquo;t recommend internships enough, especially if you&amp;rsquo;re at the university level. At this point, you can put experience with ROS and Ubuntu Core on your resume, which will help you stand out from the pack!&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re looking for ways to continue hacking on this robot using ROS, I suggest doing some research into ways to obtain the wheel speeds. Once you have that, you can start investigating morphing your driver node into something you can use with the &lt;a href=&#34;https://wiki.ros.org/diff_drive_controller&#34;&gt;diff_drive_controller&lt;/a&gt;, which goes back to &lt;a href=&#34;https://wiki.ros.org/ros_control&#34;&gt;ros_control&lt;/a&gt;, which we discussed in &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-the-driver-4-5&#34;&gt;part 4&lt;/a&gt;. Who knows, maybe I&amp;rsquo;ll write another series about that someday.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Your first robot: The driver [4/5]</title>
      <link>https://kyrofa.com/posts/your-first-robot-the-driver-4-5/</link>
      <pubDate>Fri, 09 Mar 2018 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/your-first-robot-the-driver-4-5/</guid>
      <description>UPDATE: I&amp;rsquo;m leaving this series up for historical purposes, but please note that I no longer recommend Ubuntu Core or snaps for use in robotics.
This is the fourth blog post in this series about creating your first robot with ROS and Ubuntu Core. In the previous post we worked on getting data out of the wireless controller and into ROS in a format meant for controlling differential drive robots like ours: the Twist message.</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;UPDATE: I&amp;rsquo;m leaving this series up for historical purposes, but please note that I no longer recommend Ubuntu Core or snaps for use in robotics.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is the fourth blog post in &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-a-beginner-s-guide-to-ros-and-ubuntu-core-1-5&#34;&gt;this series about creating your first robot with ROS and Ubuntu Core&lt;/a&gt;. In the &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-the-controller-3-5&#34;&gt;previous post&lt;/a&gt; we worked on getting data out of the wireless controller and into ROS in a format meant for controlling differential drive robots like ours: the &lt;code&gt;Twist&lt;/code&gt; message. Today we&amp;rsquo;re going to create a ROS node that takes that &lt;code&gt;Twist&lt;/code&gt; message and turns it into the appropriate wheel speeds to drive our robot.&lt;/p&gt;
&lt;p&gt;Remember that this is also a video series, feel free to watch the video version of this post:&lt;/p&gt;

&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
  &lt;iframe src=&#34;https://www.youtube-nocookie.com/embed/xj36BOkqZV8&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; allowfullscreen title=&#34;YouTube Video&#34;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;Alright, let&amp;rsquo;s get to it. There are a number of ways we could tackle this problem. If we were working on making our robot autonomous, we might use &lt;a href=&#34;https://wiki.ros.org/ros_control&#34;&gt;ros_control&lt;/a&gt;. However, it would add several more parts to this series, and we just don&amp;rsquo;t have the sensors available to make it worthwhile. Let me explain.&lt;/p&gt;
&lt;h2 id=&#34;open-loop-versus-closed-loop&#34;&gt;Open-loop versus closed-loop&lt;/h2&gt;
&lt;p&gt;Controlling a robot is always a loop of tasks done over and over again. For example, once we&amp;rsquo;re done with this blog post, our loop will look something like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Read data from controller&lt;/li&gt;
&lt;li&gt;Turn controller data into velocity data&lt;/li&gt;
&lt;li&gt;Turn velocity data into wheel speeds&lt;/li&gt;
&lt;li&gt;Turn wheel speeds into duty cycles&lt;/li&gt;
&lt;li&gt;Change the wheel duty cycle&lt;/li&gt;
&lt;li&gt;Return to step 1&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There are two types of control loops: &lt;em&gt;open&lt;/em&gt; or &lt;em&gt;closed&lt;/em&gt;. The difference is that a &lt;em&gt;closed&lt;/em&gt; loop has feedback in it. As a simple example, let&amp;rsquo;s say you wanted to make some hot cocoa (it&amp;rsquo;s that time of year!) in the microwave. If you have a microwave like mine, you put the cocoa in, set the timer for a few minutes and come back when it&amp;rsquo;s done. The control loop of that microwave looks something like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Set power level to 10&lt;/li&gt;
&lt;li&gt;Is time up yet? If not, return to step 1&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There&amp;rsquo;s no feedback mechanism here: this is an example of &lt;em&gt;open&lt;/em&gt;-loop control. The microwave is heating the cocoa only for a set amount of time, and that&amp;rsquo;s all it cares about. In fact, I often find it not hot enough and end up putting it back in.&lt;/p&gt;
&lt;p&gt;However, maybe you have a fancy microwave. Maybe it includes a thermal sensor of some kind, perhaps a probe that you insert into the cocoa when you put it inside the microwave (gross, let&amp;rsquo;s say it&amp;rsquo;s infrared instead). Now the microwave can determine the temperature of the cocoa, which means you no longer have to set a timer, you just need to tell the microwave how hot you like your cocoa. Insert your desired temperature, and now the microwave&amp;rsquo;s control loop might look something like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Set power level to 10&lt;/li&gt;
&lt;li&gt;How hot is the cocoa? Has it yet reached the desired temperature? If not, return to step 1&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That thermal sensor gives the microwave some data for feedback in its loop, which means this is &lt;em&gt;closed&lt;/em&gt;-loop control. This is probably the simplest one you can imagine, but there&amp;rsquo;s an entire field dedicated to closed-loop control, called &lt;a href=&#34;https://en.wikipedia.org/wiki/Control_theory&#34;&gt;control theory&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;On our robot, we have no sensors that reasonably give us a way to determine a change in position. For example, if we had sensors on the wheels that told us how quickly they were spinning, we could use our knowledge of the wheel size to estimate how fast we&amp;rsquo;re going (this is called &lt;a href=&#34;https://en.wikipedia.org/wiki/Odometry&#34;&gt;odometry&lt;/a&gt;). Since we have no such data, we have nothing to feed back: we&amp;rsquo;re limited to open-loop control right now.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s why using ros_control isn&amp;rsquo;t worth the effort: we don&amp;rsquo;t have the data necessary to close the loop, so we might as well just write our own super-simple open-loop controller instead. So let&amp;rsquo;s get on with it, shall we?&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s really only one new prerequisite here: a little more Python knowledge. We&amp;rsquo;ve kept it as simple as we could until now, but it&amp;rsquo;s time for our code to grow up a little. You&amp;rsquo;re already familiar with functions after following the worksheets. Today we&amp;rsquo;re going to use classes. Read &lt;a href=&#34;https://docs.python.org/2/tutorial/classes.html&#34;&gt;this tutorial on classes&lt;/a&gt;, if needed.&lt;/p&gt;
&lt;h2 id=&#34;step-1-calculate-wheel-speeds-from-twist&#34;&gt;Step 1: Calculate wheel speeds from Twist&lt;/h2&gt;
&lt;p&gt;Thanks to &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-the-controller-3-5&#34;&gt;part 3&lt;/a&gt;, we have the controller generating &lt;code&gt;Twist&lt;/code&gt; messages, which represent the desired linear and angular velocity of the robot (&amp;ldquo;go this fast forward/backward&amp;rdquo;, &amp;ldquo;turn this fast left/right&amp;rdquo;). At the end of the day, the only way the robot moves at all is by changing wheel direction and speed. We need to develop a way to convert the commanded velocities into wheel speeds that actually accomplish what was commanded. To do that, we need just a smidge of math.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s talk about the linear velocity first, since it&amp;rsquo;s pretty simple. If we command the robot to move forward at 1 meter per second (m/s), in what direction (forward or backward) and at what speed (in m/s) does each wheel need to move? The answer should be intuitive: both wheels need to spin forward at 1m/s. So the formula for the linear velocity part of the wheel velocity is simply:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/v=x.png&#34; alt=&#34;v=x&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;Angular velocity is a little more tricky.&lt;/p&gt;
&lt;p&gt;If we command the robot to turn left at 90 degrees per second, in what direction (forward or backward) would each wheel need to turn? The answer is fairly intuitive, but it depends on how we want to make our robot turn. There are two options: single-wheel turning and double-wheel turning.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/wheel-turning.png&#34; alt=&#34;wheel turning&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;As you can see in the picture, above, single-wheel turning involves leaving one wheel stationary, and turning the other. Double-wheel turning involves rotating both wheels in opposite directions, thus sharing the work of the turn. If we elected to use single-wheel turning, then the answer to the question would be &amp;ldquo;The left wheel doesn&amp;rsquo;t turn at all, and the right wheel turns forward.&amp;rdquo; However, I prefer the double-wheel method, so my answer is &amp;ldquo;The left wheel turns backward, and the right wheel turns forward.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/wheel-turn-90.png&#34; alt=&#34;wheel turn 90&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;Okay, we have the wheel directions. Now for the speed: if we command the robot to turn left at 90 degrees per second, at what speed (in meters per second) would each wheel need to turn? Note that this turn is a portion of a circle, the outside of which traces the desired path for our wheels. Recall the formula for the circumference of a circle:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/circumference.png&#34; alt=&#34;circumference&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;How big of a portion of the circle are we wanting to turn? Well, we know that 360 degrees is an entire circle, so we can use a ratio and combine it with the circumference to create our formula for wheel speed (which is really just the formula for the length of an arc):&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/length-of-arc.png&#34; alt=&#34;length of arc&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;Alright, there&amp;rsquo;s only one more unknown in this formula: the radius. What is it? You can see that it&amp;rsquo;s actually half the distance between the two wheels (called the &amp;ldquo;track&amp;rdquo;). So our formula turns into this:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/velocity.png&#34; alt=&#34;velocity&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;Awesome! Using this, we can now answer the question. I measured my robot&amp;rsquo;s track to be 0.091 meters. Using that, I calculate that the left wheel should be turning backward at 0.071 m/s, and the right wheel should be turning forward at the same speed.&lt;/p&gt;
&lt;p&gt;We can actually simplify this formula, because the &lt;code&gt;Twist&lt;/code&gt; message specifies angular velocities in &lt;a href=&#34;https://en.wikipedia.org/wiki/Radian&#34;&gt;radians&lt;/a&gt; per second as opposed to degrees. 360 degrees = 2π radians. If we change our ratio to use radians, things start canceling beautifully:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/simplified-velocity.png&#34; alt=&#34;simplified velocity&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;In addition, &lt;a href=&#34;http://www.ros.org/reps/rep-0103.html&#34;&gt;ROS conventions&lt;/a&gt; follow the &lt;a href=&#34;https://en.wikipedia.org/wiki/Right-hand_rule&#34;&gt;right-hand rule&lt;/a&gt;, which means that a positive angular velocity means a counter-clockwise turn, and a negative angular velocity means a clockwise turn. Let&amp;rsquo;s decide right now that a positive value means that our wheels turn forward, and a negative value turns them backward. Using these two facts along with our formulas, we can come up with formulas for both wheel speeds:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/wheel-speeds.png&#34; alt=&#34;wheel speeds&#34;  /&gt;
&lt;/p&gt;
&lt;h2 id=&#34;step-2-convert-wheel-speeds-into-duty-cycles&#34;&gt;Step 2: Convert wheel speeds into duty cycles&lt;/h2&gt;
&lt;p&gt;We got most of the math out of the way, but now we have a small issue that requires our attention. Our wheel speeds are in meters per second, but as you learned in CamJam worksheet #7, the way we actually control the motors is by applying a duty cycle between 0 and 100 (stopped and full speed, respectively). How do we get from meters per second to duty cycles? This is where &lt;em&gt;closed&lt;/em&gt;-loop control would come in handy: if we had wheel speed sensors, we could compare how fast the robot was going to how fast it should be going, and say &amp;ldquo;it needs a higher duty cycle&amp;rdquo; or &amp;ldquo;it needs a lower duty cycle&amp;rdquo;. However, we already discussed that this needs to be an &lt;em&gt;open&lt;/em&gt;-loop controller. We need to get our duty cycle by making some assumptions instead of using feedback data. We&amp;rsquo;ll do this by determining our robot&amp;rsquo;s maximum possible speed, and obtain the duty cycle by dividing the requested speed by the maximum speed.&lt;/p&gt;
&lt;p&gt;How do we determine our robot&amp;rsquo;s maximum speed? The most accurate way would be to actually measure it. Essentially, measure out a meter, set the vehicle at the beginning, get out your stopwatch, and do a drag race.&lt;/p&gt;
&lt;p&gt;Just for simplicity&amp;rsquo;s sake, though, we can cheat a little. If you noticed from &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-the-controller-3-5&#34;&gt;part 3&lt;/a&gt;, by default the data from the controller has a maximum value of 0.5. So if we see a 0.5, we know the controller is maxed out. So if we just assume that our robot&amp;rsquo;s maximum speed is 0.5 meters per second, maxing out the controller will also max out our robot&amp;rsquo;s speed. It&amp;rsquo;s a little dirty, but it works for our case since we&amp;rsquo;re only using a controller anyway.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/duty-cycle.png&#34; alt=&#34;duty cycle&#34;  /&gt;
&lt;/p&gt;
&lt;h2 id=&#34;step-3-add-geometry_msgs-as-a-dependency&#34;&gt;Step 3: Add geometry_msgs as a dependency&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;re about to rewrite the &amp;ldquo;driver&amp;rdquo; node we started in part 2 to handle &lt;code&gt;Twist&lt;/code&gt; messages instead of &lt;code&gt;String&lt;/code&gt; messages. This requires changing the &lt;code&gt;std_msgs&lt;/code&gt; dependency of our &lt;code&gt;edukit_bot&lt;/code&gt; package to &lt;code&gt;geometry_msgs&lt;/code&gt;. Open up the &lt;code&gt;edukit_bot&lt;/code&gt; package&amp;rsquo;s &lt;code&gt;package.xml&lt;/code&gt; file and make it look something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;package&amp;gt;&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;edukit_bot&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;0.1.0&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;description&amp;gt;&lt;/span&gt;The edukit_bot package&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;

 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;maintainer&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;email=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;you@you.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;You&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/maintainer&amp;gt;&lt;/span&gt;

 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;license&amp;gt;&lt;/span&gt;GPLv3&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/license&amp;gt;&lt;/span&gt;

 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;buildtool_depend&amp;gt;&lt;/span&gt;catkin&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/buildtool_depend&amp;gt;&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;build_depend&amp;gt;&lt;/span&gt;rospy&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/build_depend&amp;gt;&lt;/span&gt;

 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;exec_depend&amp;gt;&lt;/span&gt;rospy&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/exec_depend&amp;gt;&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;exec_depend&amp;gt;&lt;/span&gt;geometry_msgs&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/exec_depend&amp;gt;&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;exec_depend&amp;gt;&lt;/span&gt;python-rpi.gpio&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/exec_depend&amp;gt;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/package&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is saying &amp;ldquo;I require &lt;code&gt;catkin&lt;/code&gt; and &lt;code&gt;rospy&lt;/code&gt; in order to build, and I require &lt;code&gt;rospy&lt;/code&gt;, &lt;code&gt;geometry_msgs&lt;/code&gt;, and &lt;code&gt;python-rpi.gpio&lt;/code&gt; in order to run.&amp;rdquo;&lt;/p&gt;
&lt;h2 id=&#34;step-4-write-the-driver&#34;&gt;Step 4: Write the driver&lt;/h2&gt;
&lt;p&gt;Now that we have the math out of the way, let&amp;rsquo;s write the ROS driver that utilizes it. In fact, we&amp;rsquo;ll rewrite the &amp;ldquo;driver&amp;rdquo; node in the &lt;code&gt;edukit_bot&lt;/code&gt; package that we started in &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-introduction-to-the-robot-operating-system-2-5&#34;&gt;part 2&lt;/a&gt;. First, activate that workspace:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/edukit_bot_ws
$ source devel/setup.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now make the &lt;code&gt;~/edukit_bot_ws/src/edukit_bot/src/driver_node&lt;/code&gt; file look like this (note that the entire package created in this part is &lt;a href=&#34;https://github.com/kyrofa/your-first-robot/tree/master/part_4/edukit_bot&#34;&gt;available for reference&lt;/a&gt;):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/env python&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; rospy
&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; geometry_msgs.msg &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Twist

&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; RPi.GPIO &lt;span style=&#34;color:#f92672&#34;&gt;as&lt;/span&gt; GPIO

&lt;span style=&#34;color:#75715e&#34;&gt;# Set the GPIO modes&lt;/span&gt;
GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;setmode(GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;BCM)
GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;setwarnings(False)

_FREQUENCY &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;

&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_clip&lt;/span&gt;(value, minimum, maximum):
   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Ensure value is between minimum and maximum.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;

   &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; value &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; minimum:
       &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; minimum
   &lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; value &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; maximum:
       &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; maximum
   &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; value

&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Motor&lt;/span&gt;:
   &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; __init__(self, forward_pin, backward_pin):
       GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;setup(forward_pin, GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;OUT)
       GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;setup(backward_pin, GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;OUT)

       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_forward_pwm &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;PWM(forward_pin, _FREQUENCY)
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_backward_pwm &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;PWM(backward_pin, _FREQUENCY)

   &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;move&lt;/span&gt;(self, speed_percent):
       speed &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; _clip(abs(speed_percent), &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;)

       &lt;span style=&#34;color:#75715e&#34;&gt;# Positive speeds move wheels forward, negative speeds&lt;/span&gt;
       &lt;span style=&#34;color:#75715e&#34;&gt;# move wheels backward&lt;/span&gt;
       &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; speed_percent &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
           self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_backward_pwm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start(speed)
           self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_forward_pwm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
       &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
           self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_forward_pwm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start(speed)
           self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_backward_pwm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)

&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Driver&lt;/span&gt;:
   &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; __init__(self):
       rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;init_node(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;driver&amp;#39;&lt;/span&gt;)

       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_last_received &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get_time()
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_timeout &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get_param(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;~timeout&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_rate &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get_param(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;~rate&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;)
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_max_speed &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get_param(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;~max_speed&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0.5&lt;/span&gt;)
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_wheel_base &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get_param(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;~wheel_base&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0.091&lt;/span&gt;)

       &lt;span style=&#34;color:#75715e&#34;&gt;# Assign pins to motors. These may be distributed&lt;/span&gt;
       &lt;span style=&#34;color:#75715e&#34;&gt;# differently depending on how you&amp;#39;ve built your robot&lt;/span&gt;
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_left_motor &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Motor(&lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;)
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_right_motor &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Motor(&lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;)
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_left_speed_percent &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_right_speed_percent &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;

       &lt;span style=&#34;color:#75715e&#34;&gt;# Setup subscriber for velocity twist message&lt;/span&gt;
       rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Subscriber(
           &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cmd_vel&amp;#39;&lt;/span&gt;, Twist, self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_velocity_received_callback)

   &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_velocity_received_callback&lt;/span&gt;(self, message):
       &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Handle new velocity command message.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;

       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_last_received &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get_time()

       &lt;span style=&#34;color:#75715e&#34;&gt;# Extract linear and angular velocities from the message&lt;/span&gt;
       linear &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; message&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;linear&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;x
       angular &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; message&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;angular&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;z

       &lt;span style=&#34;color:#75715e&#34;&gt;# Calculate wheel speeds in m/s&lt;/span&gt;
       left_speed &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; linear &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; angular&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_wheel_base&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
       right_speed &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; linear &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; angular&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_wheel_base&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;

       &lt;span style=&#34;color:#75715e&#34;&gt;# Ideally we&amp;#39;d now use the desired wheel speeds along&lt;/span&gt;
       &lt;span style=&#34;color:#75715e&#34;&gt;# with data from wheel speed sensors to come up with the&lt;/span&gt;
       &lt;span style=&#34;color:#75715e&#34;&gt;# power we need to apply to the wheels, but we don&amp;#39;t have&lt;/span&gt;
       &lt;span style=&#34;color:#75715e&#34;&gt;# wheel speed sensors. Instead, we&amp;#39;ll simply convert m/s&lt;/span&gt;
       &lt;span style=&#34;color:#75715e&#34;&gt;# into percent of maximum wheel speed, which gives us a&lt;/span&gt;
       &lt;span style=&#34;color:#75715e&#34;&gt;# duty cycle that we can apply to each motor.&lt;/span&gt;
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_left_speed_percent &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; left_speed&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_max_speed)
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_right_speed_percent &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; right_speed&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_max_speed)

   &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;run&lt;/span&gt;(self):
       &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;The control loop of the driver.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;

       rate &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Rate(self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_rate)

       &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;is_shutdown():
           &lt;span style=&#34;color:#75715e&#34;&gt;# If we haven&amp;#39;t received new commands for a while, we&lt;/span&gt;
           &lt;span style=&#34;color:#75715e&#34;&gt;# may have lost contact with the commander-- stop&lt;/span&gt;
           &lt;span style=&#34;color:#75715e&#34;&gt;# moving&lt;/span&gt;
           delay &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get_time() &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_last_received
           &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; delay &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_timeout:
               self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_left_motor&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;move(self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_left_speed_percent)
               self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_right_motor&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;move(self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_right_speed_percent)
           &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
               self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_left_motor&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;move(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
               self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_right_motor&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;move(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)

           rate&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sleep()

&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
   driver &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Driver()

   &lt;span style=&#34;color:#75715e&#34;&gt;# Run driver. This will block&lt;/span&gt;
   driver&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;run()

   GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cleanup()

&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;:
   main()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Okay, that probably looks pretty new. Why don&amp;rsquo;t we break it down piece by piece.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/env python&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; rospy
&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; geometry_msgs.msg &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Twist

&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; RPi.GPIO &lt;span style=&#34;color:#f92672&#34;&gt;as&lt;/span&gt; GPIO

&lt;span style=&#34;color:#75715e&#34;&gt;# Set the GPIO modes&lt;/span&gt;
GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;setmode(GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;BCM)
GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;setwarnings(False)

_FREQUENCY &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is more or less straight out of &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-introduction-to-the-robot-operating-system-2-5&#34;&gt;part 2&lt;/a&gt;, although we&amp;rsquo;re importing the &lt;code&gt;Twist&lt;/code&gt; message now instead of &lt;code&gt;String&lt;/code&gt;. We&amp;rsquo;re also saving the frequency into a more conventionally-named variable, &lt;code&gt;_FREQUENCY&lt;/code&gt; (the underscore indicates that it&amp;rsquo;s for internal use only, all-caps indicates that it&amp;rsquo;s a constant).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_clip&lt;/span&gt;(value, minimum, maximum):
   &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Ensure value is between minimum and maximum.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;

   &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; value &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; minimum:
       &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; minimum
   &lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; value &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; maximum:
       &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; maximum
   &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; value
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;_clip&lt;/code&gt; function is pretty simple: it makes sure a given value is between the given minimum and maximum. We&amp;rsquo;ll use it later to make sure we don&amp;rsquo;t try to make the motors move with a duty cycle less than 0 or greater than 100.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Motor&lt;/span&gt;:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we&amp;rsquo;re creating a new class to represent a motor that we can move.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; __init__(self, forward_pin, backward_pin):
       GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;setup(forward_pin, GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;OUT)
       GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;setup(backward_pin, GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;OUT)

       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_forward_pwm &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;PWM(forward_pin, _FREQUENCY)
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_backward_pwm &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;PWM(backward_pin, _FREQUENCY)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is the initializer for the &lt;code&gt;Motor&lt;/code&gt; class. It&amp;rsquo;s called automatically whenever a new instance is created. It accepts two parameters, one for each pin involved in moving the motor (one forward, one backward). It then sets the pins up as outputs, and saves off the PWMs for use in the &lt;code&gt;move()&lt;/code&gt; function:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;move&lt;/span&gt;(self, speed_percent):
       speed &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; _clip(abs(speed_percent), &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;)

       &lt;span style=&#34;color:#75715e&#34;&gt;# Positive speeds move wheels forward, negative speeds&lt;/span&gt;
       &lt;span style=&#34;color:#75715e&#34;&gt;# move wheels backward&lt;/span&gt;
       &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; speed_percent &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
           self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_backward_pwm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start(speed)
           self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_forward_pwm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
       &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
           self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_forward_pwm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start(speed)
           self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_backward_pwm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is the main worker function of the &lt;code&gt;Motor&lt;/code&gt; class: it&amp;rsquo;s how the motor moves. It accepts a positive or negative percent (between 0-100), where a positive value moves forward, and a negative value moves backward. Here&amp;rsquo;s where we utilize the &lt;code&gt;_clip()&lt;/code&gt; function we discussed above to ensure the percentage requested does not fall outside the valid range of 0-100, which turns it into a valid duty cycle.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Driver&lt;/span&gt;:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s one more class, representing the ROS driver itself.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; __init__(self):
       rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;init_node(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;driver&amp;#39;&lt;/span&gt;)

       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_last_received &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get_time()
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_timeout &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get_param(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;~timeout&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_rate &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get_param(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;~rate&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;)
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_max_speed &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get_param(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;~max_speed&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0.5&lt;/span&gt;)
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_wheel_base &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get_param(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;~wheel_base&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0.091&lt;/span&gt;)

       &lt;span style=&#34;color:#75715e&#34;&gt;# Assign pins to motors. These may be distributed&lt;/span&gt;
       &lt;span style=&#34;color:#75715e&#34;&gt;# differently depending on how you&amp;#39;ve built your robot&lt;/span&gt;
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_left_motor &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Motor(&lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;)
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_right_motor &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Motor(&lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;)
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_left_speed_percent &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_right_speed_percent &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;

       &lt;span style=&#34;color:#75715e&#34;&gt;# Setup subscriber for velocity twist message&lt;/span&gt;
       rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Subscriber(
           &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cmd_vel&amp;#39;&lt;/span&gt;, Twist, self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_velocity_received_callback)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s the initializer for the &lt;code&gt;Driver&lt;/code&gt; class, called automatically when a new instance is created. It doesn&amp;rsquo;t accept any parameters, since it supports changing its behavior by way of parameters from the ROS Parameter Server (which you learned a bit about in &lt;a href=&#34;http://wiki.ros.org/ROS/Tutorials/UnderstandingServicesParams&#34;&gt;ROS tutorial number 7&lt;/a&gt;). We start out by initializing the ROS node, which begins communication with the ROS master. We then record the current time (which we&amp;rsquo;ll use later) and retrieve the values for all the parameters we support. After that, we create two instances of the &lt;code&gt;Motor&lt;/code&gt; class defined above to represent the left and right motors, and initialize our speeds to zero.&lt;/p&gt;
&lt;p&gt;A quick aside: these pins correspond to which motor is motor A, which is motor B, and the polarity that we used to hook them up. If you happened to connect yours differently than I connected mine (which is perfectly fine), you may need to switch these pins around a little or your robot will move hilariously badly.&lt;/p&gt;
&lt;p&gt;Finally, we subscribe to our &lt;code&gt;Twist&lt;/code&gt; topic, which is called &lt;code&gt;cmd_vel&lt;/code&gt; (following convention, it means &amp;ldquo;commanded velocity&amp;rdquo;), and request that the &lt;code&gt;_velocity_received_callback()&lt;/code&gt; function is called whenever a new command is received.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_velocity_received_callback&lt;/span&gt;(self, message):
       &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Handle new velocity command message.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;

       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_last_received &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get_time()

       &lt;span style=&#34;color:#75715e&#34;&gt;# Extract linear and angular velocities from the message&lt;/span&gt;
       linear &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; message&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;linear&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;x
       angular &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; message&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;angular&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;z

       &lt;span style=&#34;color:#75715e&#34;&gt;# Calculate wheel speeds in m/s&lt;/span&gt;
       left_speed &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; linear &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; angular&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_wheel_base&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
       right_speed &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; linear &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; angular&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_wheel_base&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;

       &lt;span style=&#34;color:#75715e&#34;&gt;# Ideally we&amp;#39;d now use the desired wheel speeds along&lt;/span&gt;
       &lt;span style=&#34;color:#75715e&#34;&gt;# with data from wheel speed sensors to come up with the&lt;/span&gt;
       &lt;span style=&#34;color:#75715e&#34;&gt;# power we need to apply to the wheels, but we don&amp;#39;t have&lt;/span&gt;
       &lt;span style=&#34;color:#75715e&#34;&gt;# wheel speed sensors. Instead, we&amp;#39;ll simply convert m/s&lt;/span&gt;
       &lt;span style=&#34;color:#75715e&#34;&gt;# into percent of maximum wheel speed, which gives us a&lt;/span&gt;
       &lt;span style=&#34;color:#75715e&#34;&gt;# duty cycle that we can apply to each motor.&lt;/span&gt;
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_left_speed_percent &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; left_speed&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_max_speed)
       self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_right_speed_percent &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; right_speed&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_max_speed)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is the &lt;code&gt;_velocity_received_callback()&lt;/code&gt; function, part of the &lt;code&gt;Driver&lt;/code&gt; class. Thanks to our subscription in &lt;code&gt;__init__()&lt;/code&gt;, this function is called whenever a new command message comes in. Here&amp;rsquo;s where the math we did in steps 1 and 2 come into play. First of all, we record the time we received the message (which we&amp;rsquo;ll discuss in a moment). Then we extract the linear and angular velocity components out of the message. Using this information, we use the wheel speed formula we derived in step 1 to calculate the left and right wheel speeds (in meters per second). Then we use the duty cycle formula derived in step 2 to turn those wheel speeds into left and right percentages. Since these values can be negative, they represent both the desired duty cycle as well as wheel direction.&lt;/p&gt;
&lt;p&gt;Note that this function doesn&amp;rsquo;t actually change the wheel speeds at all, it just calculates what they should be. Why? We&amp;rsquo;ll discuss that in a moment.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;run&lt;/span&gt;(self):
       &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;The control loop of the driver.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;

       rate &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Rate(self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_rate)

       &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;is_shutdown():
           &lt;span style=&#34;color:#75715e&#34;&gt;# If we haven&amp;#39;t received new commands for a while, we&lt;/span&gt;
           &lt;span style=&#34;color:#75715e&#34;&gt;# may have lost contact with the commander-- stop&lt;/span&gt;
           &lt;span style=&#34;color:#75715e&#34;&gt;# moving&lt;/span&gt;
           delay &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get_time() &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_last_received
           &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; delay &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_timeout:
               self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_left_motor&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;move(self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_left_speed_percent)
               self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_right_motor&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;move(self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_right_speed_percent)
           &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
               self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_left_motor&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;move(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
               self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_right_motor&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;move(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)

           rate&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sleep()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is the control loop of our &lt;code&gt;Driver&lt;/code&gt; class. It&amp;rsquo;s where we actually apply the newly-calculated wheel speeds, in a loop that runs at a specific rate (10 Hz by default). What if we don&amp;rsquo;t receive a new commanded velocity by the time we loop again? We just use the previously-commanded one. That seems a little odd&amp;ndash; why do we have a loop at all if we only ever use the commanded velocity?&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s say we pulled this logic out of the loop and put it in the message handler (the &lt;code&gt;_velocity_received_callback()&lt;/code&gt; function) instead. What happens if we received a few commanded velocities, but then the network went down, or we otherwise lost communication with the controller? The robot would just continue moving at the previously-commanded velocities, and you would have to go chase it down. I&amp;rsquo;ve had that happen on a robot that weighed several hundred pounds&amp;ndash; it went out of range of the control unit and just kept on trucking. My legs were sore for a week.&lt;/p&gt;
&lt;p&gt;By putting this logic in a loop, and recording when commanded messages come in, we can implement a timeout to make sure our robot never runs away from us if issues occur. You can see this in the loop: we calculate how long it&amp;rsquo;s been since we last received a commanded velocity. If it&amp;rsquo;s less than the timeout (2 seconds by default) the calculated wheel speeds are used. If it&amp;rsquo;s greater than the timeout (i.e. we haven&amp;rsquo;t received a message for a while), then we halt the robot.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
   driver &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Driver()

   &lt;span style=&#34;color:#75715e&#34;&gt;# Run driver. This will block&lt;/span&gt;
   driver&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;run()

   GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cleanup()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is the main part of the program, where we simply create a new instance of the &lt;code&gt;Driver&lt;/code&gt; class, start its control loop, and clean up when it&amp;rsquo;s done.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;:
   main()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is the entry point of this whole node&amp;ndash; we simply run the &lt;code&gt;main()&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;Alright, we&amp;rsquo;re done with this node. Let&amp;rsquo;s build it before moving on:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/edukit_bot_ws
$ catkin_make
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;step-5-test-the-driver-node&#34;&gt;Step 5: Test the driver node&lt;/h2&gt;
&lt;p&gt;As in &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-introduction-to-the-robot-operating-system-2-5&#34;&gt;part 2&lt;/a&gt;, we need to make sure we have permission to access GPIO as a user (remember this resets upon reboot):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo chmod a+rw /dev/gpiomem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;re also going to use what we learned in &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-the-controller-3-5&#34;&gt;part 3&lt;/a&gt; to get the controller giving us &lt;code&gt;Twist&lt;/code&gt; messages. Open up four terminals running the classic shell. In the first one, run &lt;code&gt;roscore&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ roscore
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the second, run the joy node:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ rosrun joy joy_node
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1515003691.568172834&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Opened joystick: /dev/input/js0. deadzone_: 0.050000.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the third, run the teleop node:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ rosrun teleop_twist_joy teleop_node
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1515003734.469189240&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Teleop enable button 0.
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1515003734.469664500&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Linear axis x on &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; at scale 0.500000.
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1515003734.469924707&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Angular axis yaw on &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; at scale 0.500000.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And finally, in the fourth terminal activate our workspace and run our new driver node:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/edukit_bot_ws
$ source devel/setup.sh
$ rosrun edukit_bot driver_node
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now turn on your controller and start moving your robot around! Oh wait, you tried to turn and nothing happened? Right, let me explain.&lt;/p&gt;
&lt;p&gt;Remember in step 2 how we used the fact that the twist messages we were getting with the controller maxed out at 0.5? That works great for linear velocities, but the controller also maxes out at 0.5 radians per second for its angular velocity, which is so slow that the motors can&amp;rsquo;t actually pull it off (they just give off a quiet buzzing sound instead, as if they want you to know that they&amp;rsquo;re doing their best).&lt;/p&gt;
&lt;p&gt;The solution is to request the teleop node to scale those values to more reasonable turning speeds. Go to the terminal where you ran the teleop node, ctrl+c it, and re-run it with an angular scaling factor. You can experiment with this to see how responsive/twitchy you like your robot, but I settled on a value of 4 (larger values will make it more sensitive):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ rosrun teleop_twist_joy teleop_node _scale_angular:&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1515005430.818803229&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Teleop enable button 0.
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1515005430.819282707&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Linear axis x on &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; at scale 0.500000.
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1515005430.819531978&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Angular axis yaw on &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; at scale 4.000000.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;NOW you should be able to successfully drive it around.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-sharing-with-others-5-5&#34;&gt;next (and final) post in this series&lt;/a&gt;, we&amp;rsquo;ll talk about how launch files save us from having to open a billion terminals just to run a few ROS nodes, and turn our ROS package into a snap that starts on boot and is easily installed by your friends!&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Your first robot: The controller [3/5]</title>
      <link>https://kyrofa.com/posts/your-first-robot-the-controller-3-5/</link>
      <pubDate>Sat, 10 Feb 2018 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/your-first-robot-the-controller-3-5/</guid>
      <description>UPDATE: I&amp;rsquo;m leaving this series up for historical purposes, but please note that I no longer recommend Ubuntu Core or snaps for use in robotics.
This is the third blog post in this series about creating your first robot with ROS and Ubuntu Core. In the previous post you were introduced to the Robot Operating System (ROS), and got your robot moving by ROSifying one of the CamJam worksheets. Today we&amp;rsquo;re going to move beyond the CamJam worksheets, and work toward having our robot remotely controlled by focusing on our wireless controller: getting data out of it and into ROS messages.</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;UPDATE: I&amp;rsquo;m leaving this series up for historical purposes, but please note that I no longer recommend Ubuntu Core or snaps for use in robotics.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is the third blog post in &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-a-beginner-s-guide-to-ros-and-ubuntu-core-1-5&#34;&gt;this series about creating your first robot with ROS and Ubuntu Core&lt;/a&gt;. In the &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-introduction-to-the-robot-operating-system-2-5&#34;&gt;previous post&lt;/a&gt; you were introduced to the Robot Operating System (ROS), and got your robot moving by ROSifying one of the CamJam worksheets. Today we&amp;rsquo;re going to move beyond the CamJam worksheets, and work toward having our robot remotely controlled by focusing on our wireless controller: getting data out of it and into ROS messages.&lt;/p&gt;
&lt;p&gt;As I mentioned in part 1, I&amp;rsquo;m using the &lt;a href=&#34;https://thepihut.com/collections/raspberry-pi-gaming/products/raspberry-pi-compatible-wireless-gamepad-controller?variant=38135423121&#34;&gt;wireless controller from the Pi Hut&lt;/a&gt;, but really the only requirement to follow this post exactly is that it shows up as &lt;code&gt;/dev/input/jsX&lt;/code&gt; on the Pi (where &lt;code&gt;X&lt;/code&gt; is some number, typically 0). I have a wired Xbox 360 controller that works the same way, for example.&lt;/p&gt;
&lt;p&gt;Alright, let&amp;rsquo;s get started! Remember that this is also a video series, feel free to watch the video version of this post:&lt;/p&gt;

&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
  &lt;iframe src=&#34;https://www.youtube-nocookie.com/embed/xRK-tOgzeUo&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; allowfullscreen title=&#34;YouTube Video&#34;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&#34;step-1-get-data-out-of-the-controller&#34;&gt;Step 1: Get data out of the controller&lt;/h2&gt;
&lt;p&gt;One of the benefits of using ROS is that a lot of the problems are already solved for us. In this case, there&amp;rsquo;s already a ROS node that exists solely to read data from controllers like ours, called &lt;a href=&#34;https://wiki.ros.org/joy&#34;&gt;joy&lt;/a&gt;. Get into your classic shell, and install it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt install ros-kinetic-joy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Insert the USB receiver for the controller into the Pi, and you should see it appear as &lt;code&gt;/dev/input/js0&lt;/code&gt;. Open up two others terminals, and get into your classic shell in each (so now you have three). On one terminal, run the master:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ roscore
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On another terminal, run the joy node:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ rosrun joy joy_node
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1513274242.219112701&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Opened joystick: /dev/input/js0. deadzone_: 0.050000.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On the third terminal, let&amp;rsquo;s take a look at what&amp;rsquo;s coming out of the joy node. First, let&amp;rsquo;s see what topics are available:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ rostopic list
/diagnostics
/joy
/rosout
/rosout_agg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Take note of the &lt;code&gt;/joy&lt;/code&gt; topic. That&amp;rsquo;s being published by the joy node. Let&amp;rsquo;s take a look. Run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ rostopic echo /joy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nothing should happen at first. Now turn on your controller, and start moving joysticks about and pressing buttons. You should see all sorts of stuff start spewing, looking something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;header:
seq: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
stamp:
secs: &lt;span style=&#34;color:#ae81ff&#34;&gt;1513274448&lt;/span&gt;
nsecs: &lt;span style=&#34;color:#ae81ff&#34;&gt;114444863&lt;/span&gt;
frame_id: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;
axes: &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;0.0, 0.08846031129360199, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
buttons: &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
---
header:
seq: &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
stamp:
secs: &lt;span style=&#34;color:#ae81ff&#34;&gt;1513274448&lt;/span&gt;
nsecs: &lt;span style=&#34;color:#ae81ff&#34;&gt;130450677&lt;/span&gt;
frame_id: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;
axes: &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;0.0, 0.26209455728530884, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
buttons: &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
---
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s a bunch of &lt;code&gt;Joy&lt;/code&gt; messages representing the current state of the controller: joysticks, buttons, everything. Whenever you move a stick or press a button, more messages are sent showing the state change. That saved us a bunch of work! Go ahead and &lt;code&gt;CTRL+C&lt;/code&gt; out of &lt;code&gt;rostopic&lt;/code&gt;, the &lt;code&gt;joy&lt;/code&gt; node, and &lt;code&gt;roscore&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;step-2-how-do-we-control-our-robot&#34;&gt;Step 2: How do we control our robot?&lt;/h2&gt;
&lt;p&gt;So we&amp;rsquo;re able to get data out of the controller, but that data is still somewhat raw. Remember that, at the end of the day, the robot moves and turns via changes in its wheel speeds. How do we get from these controller messages to wheel speeds? We could totally write a new node that subscribes to the controller messages, pulls out the joystick movements, and turns those into wheel speeds. However, that would tightly couple the robot driver to the controller, which would be short-sighted. Is there not a more generic way to request that the robot move and turn? Indeed there is.&lt;/p&gt;
&lt;p&gt;The answer can be found by looking forward to how we would make the robot autonomous. This would probably utilize the &lt;a href=&#34;https://wiki.ros.org/navigation&#34;&gt;ROS navigation stack&lt;/a&gt;, which was written for arbitrary robots, and outlines exactly how we should control our robot with its first hardware requirement:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;[This software] is meant for both differential drive and holonomic wheeled robots only. It assumes that the mobile base is controlled by sending desired velocity commands to achieve in the form of: x velocity, y velocity, theta velocity.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;~ The ROS Navigation Stack&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Okay, so what does this mean? The robot we have is a &lt;a href=&#34;https://en.wikipedia.org/wiki/Differential_wheeled_robot&#34;&gt;differential drive&lt;/a&gt; robot. That means it can move forward, backward, and turn left and right, all with wheels that don&amp;rsquo;t steer like your car (which would be e.g. &lt;a href=&#34;https://en.wikipedia.org/wiki/Ackermann_steering_geometry&#34;&gt;Ackermann&lt;/a&gt; steering). It does this by rotating the wheels at different speeds and/or in different directions. It cannot, however, move side-to-side. Wheeled holonomic systems are more magical in that they can move forward, backward, turn left and right, and move side-to-side. These typically use either &lt;a href=&#34;https://en.wikipedia.org/wiki/Omni_wheel&#34;&gt;omni wheels&lt;/a&gt; or &lt;a href=&#34;https://en.wikipedia.org/wiki/Mecanum_wheel&#34;&gt;mecanum wheels&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We can make this easier to understand by drawing the movements possible by each drive type:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/drive-type.jpg&#34; alt=&#34;drive type&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;On the left we have the differential drive system, like our robot. It can move in a linear fashion along the red line we labeled X (i.e. it can move forward and backward), and it can move in an angular fashion (rotate) around the green circle we labeled θ (theta). On the right we have the holonomic drive system, which can do exactly the same as the differential system but can also move along the blue line we labeled Y (i.e. it can move side-to-side).&lt;/p&gt;
&lt;p&gt;Concretely, the ROS navigation stack supports both systems by supplying commands that are velocities in each of these directions (X, Y, and θ). So let&amp;rsquo;s make our robot support those types of commands! Since our system doesn&amp;rsquo;t support Y velocities, we&amp;rsquo;ll just be using X and θ. ROS has a standard message that is used for exactly this type of data: the &lt;a href=&#34;https://docs.ros.org/api/geometry_msgs/html/msg/Twist.html&#34;&gt;Twist message&lt;/a&gt;, which consists of a set of linear and angular velocities. So let&amp;rsquo;s turn our Joy messages into Twist messages.&lt;/p&gt;
&lt;h2 id=&#34;step-3-turn-joy-messages-into-twist-messages&#34;&gt;Step 3: Turn Joy messages into Twist messages&lt;/h2&gt;
&lt;p&gt;I might sound like a bit of a broken record, but guess what: this already exists, too! It&amp;rsquo;s called &lt;a href=&#34;https://wiki.ros.org/teleop_twist_joy&#34;&gt;teleop_twist_joy&lt;/a&gt;. Let&amp;rsquo;s install it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt install ros-kinetic-teleop-twist-joy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now open four terminals running the classic shell (I know, this is getting ridiculous). In one, run &lt;code&gt;roscore&lt;/code&gt; again:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ roscore
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In another, run the joy node again:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ rosrun joy joy_node
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1513274242.219112701&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Opened joystick: /dev/input/js0. deadzone_: 0.050000.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the third, run the newly-installed teleop node:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ rosrun teleop_twist_joy teleop_node
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1513286128.076671780&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Teleop enable button 0.
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1513286128.077269945&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Linear axis x on &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; at scale 0.500000.
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1513286128.077666187&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Angular axis yaw on &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; at scale 0.500000.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And finally, in the fourth let&amp;rsquo;s poke at it. First, take a look at the available topics:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ rostopic list
/cmd_vel
/diagnostics
/joy
/rosout
/rosout_agg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We know that the &lt;code&gt;/joy&lt;/code&gt; topic contains the controller data, but &lt;code&gt;/cmd_vel&lt;/code&gt; is new. That&amp;rsquo;s the conventional name for Twist-based velocity commands. Let&amp;rsquo;s take a look at those:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ rostopic echo /cmd_vel
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As before, nothing should happen at first. Then turn on your controller and start moving the left joystick. &lt;em&gt;Psych!&lt;/em&gt; That doesn&amp;rsquo;t do anything either. Because the teleop node was written for remote control in mind, it implements a safety button as a sort of &amp;ldquo;dead-man&amp;rsquo;s switch&amp;rdquo; if you will. By default, this is button 0, which on my controller is the X button. Hold down that button, and &lt;em&gt;then&lt;/em&gt; move the left joystick, and you should start seeing velocity commands start scrolling past, like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;linear:
 x: 0.0
 y: 0.0
 z: 0.0
angular:
 x: 0.0
 y: 0.0
 z: 0.0
---
linear:
 x: 0.0225298888981
 y: 0.0
 z: 0.0
angular:
 x: 0.0
 y: 0.0
 z: 0.0
---
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;ll notice that while there are a bunch of components to this message, only two of the components actually change depending on how you move that joystick: the linear X component, and the angular Z component. This actually relates back to the picture in step 2: X corresponds to the red X line we drew, and Z corresponds to the green θ circle. So X values mean &amp;ldquo;move forward or backward this fast&amp;rdquo; and Z values mean &amp;ldquo;rotate left or right this fast&amp;rdquo;. Exactly what we need!&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-the-driver-4-5&#34;&gt;next post in this series&lt;/a&gt;, we&amp;rsquo;ll rewrite the simple &amp;ldquo;driver&amp;rdquo; node we created last time to accept &lt;code&gt;Twist&lt;/code&gt; messages and turn them into wheel speeds.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Your first robot: Introduction to the Robot Operating System [2/5]</title>
      <link>https://kyrofa.com/posts/your-first-robot-introduction-to-the-robot-operating-system-2-5/</link>
      <pubDate>Sat, 20 Jan 2018 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/your-first-robot-introduction-to-the-robot-operating-system-2-5/</guid>
      <description>UPDATE: I&amp;rsquo;m leaving this series up for historical purposes, but please note that I no longer recommend Ubuntu Core or snaps for use in robotics.
This is the second blog post in this series about creating your first robot with ROS and Ubuntu Core. In the previous post we walked through all the hardware necessary to follow this series, and introduced Ubuntu Core, the operating system for IoT devices. We installed it on our Raspberry Pi, and used it to go through the CamJam worksheets.</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;UPDATE: I&amp;rsquo;m leaving this series up for historical purposes, but please note that I no longer recommend Ubuntu Core or snaps for use in robotics.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is the second blog post in &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-a-beginner-s-guide-to-ros-and-ubuntu-core-1-5&#34;&gt;this series about creating your first robot with ROS and Ubuntu Core&lt;/a&gt;. In the &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-a-beginner-s-guide-to-ros-and-ubuntu-core-1-5&#34;&gt;previous post&lt;/a&gt; we walked through all the hardware necessary to follow this series, and introduced Ubuntu Core, the operating system for IoT devices. We installed it on our Raspberry Pi, and used it to go through the &lt;a href=&#34;http://camjam.me/?page_id=1035&#34;&gt;CamJam worksheets&lt;/a&gt;. In this post, I&amp;rsquo;m going to introduce you to the &lt;a href=&#34;http://www.ros.org/&#34;&gt;Robot Operating System (ROS)&lt;/a&gt;, and we&amp;rsquo;ll use it to move our robot. We&amp;rsquo;ll be using ROS throughout the rest of the series. Remember that this is also a video series, feel free to watch the video version of this post:&lt;/p&gt;

&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
  &lt;iframe src=&#34;https://www.youtube-nocookie.com/embed/Sw33EbZHris&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; allowfullscreen title=&#34;YouTube Video&#34;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&#34;what-is-the-robot-operating-system&#34;&gt;What is the Robot Operating System?&lt;/h2&gt;
&lt;p&gt;At its simplest, ROS is a set of open-source libraries and tools meant to ease development of robots. It also provides an infrastructure for connecting various robotic components together. For example, if you happened to go through all of the CamJam worksheets (particularly #9), you&amp;rsquo;ve written a single Python script that&amp;rsquo;s responsible for a bunch of things: controlling the motors, reading from the line detector, reading from the ultrasonic sensor, etc. What if we threw a wireless controller into the mix? That script quickly gets complicated, and if you happen to want to swap one component out for another, you need to rewrite the whole thing; i.e. these logically different components are tightly coupled together since they&amp;rsquo;re in the same script.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/ros-communication.jpg&#34; alt=&#34;ros communication&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;ROS provides a communication infrastructure that allows you to extract different logic into their own modules, and have them communicate with each other in a standard way, as depicted in the picture above. For example, if you wanted to switch ultrasonic sensors, and rewrite the &amp;ldquo;distance&amp;rdquo; module, you could do that without having to touch any of the other modules as long as the new distance module talked the same way as the old one.&lt;/p&gt;
&lt;p&gt;This will all make more sense once we dive in, so let&amp;rsquo;s get started, shall we?&lt;/p&gt;
&lt;h2 id=&#34;step-1-install-ros-on-the-raspberry-pi&#34;&gt;Step 1: Install ROS on the Raspberry Pi&lt;/h2&gt;
&lt;p&gt;As of this writing, ROS has &lt;a href=&#34;https://wiki.ros.org/Distributions&#34;&gt;three supported releases&lt;/a&gt;: Indigo Igloo, Kinetic Kame, and Lunar Loggerhead. Ubuntu Core series 16 (the one we&amp;rsquo;re using) is Ubuntu Xenial, which limits our options to Kinetic and Lunar. Lunar is technically newer and shinier, but like Ubuntu, ROS has Long-Term Support (LTS) releases that are supported for an extended period of time, and Kinetic is their most recent LTS. As a result, we&amp;rsquo;ll use Kinetic here.&lt;/p&gt;
&lt;p&gt;SSH into your Pi, and get into your classic shell:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo classic
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s follow &lt;a href=&#34;https://wiki.ros.org/kinetic/Installation/Ubuntu&#34;&gt;ROS Kinetic&amp;rsquo;s install guide&lt;/a&gt;. ROS maintains its own repository of Debian packages, which we need to add to our system:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo sh -c &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;echo &amp;#34;deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main&amp;#34; &amp;gt; /etc/apt/sources.list.d/ros-latest.list&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We then need to add that repository&amp;rsquo;s keys to the list of keys we&amp;rsquo;ll accept (this verifies that packages in that repository really do come from ROS):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt-key adv --keyserver &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;hkp://keyserver.ubuntu.com:80&amp;#39;&lt;/span&gt; --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we&amp;rsquo;ll re-index all the repositories we have configured, since we just added one:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt update
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now let&amp;rsquo;s install ROS. As you&amp;rsquo;ll see on the install guide, there are a bunch of meta packages available (packages that exist solely to pull in other packages). Let&amp;rsquo;s install the smallest, bare-bones one, &lt;code&gt;ros-kinetic-ros-base&lt;/code&gt;, which will take up around 700MB. We&amp;rsquo;ll also install &lt;code&gt;g++&lt;/code&gt;, the C++ compiler (it&amp;rsquo;s still required, even though we&amp;rsquo;re writing Python):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt install g++ ros-kinetic-ros-base
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At this point, ROS is successfully installed, but none of its tools are available to run. That&amp;rsquo;s because ROS installs itself into what it calls a &amp;ldquo;workspace&amp;rdquo;, and provides a shell script that activates that workspace. We can make sure we activate that workspace upon login by adding it to the &lt;code&gt;.bashrc&lt;/code&gt; file in our home directory:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;source /opt/ros/kinetic/setup.bash&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; ~/.bashrc
$ source ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you should be able to run &lt;code&gt;roscore&lt;/code&gt; without troubles:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ roscore
&amp;lt;snip&amp;gt;
SUMMARY
&lt;span style=&#34;color:#f92672&#34;&gt;========&lt;/span&gt;
PARAMETERS
* /rosdistro: kinetic
* /rosversion: 1.12.12
NODES
auto-starting new master
process&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;master&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: started with pid &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;4987&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
ROS_MASTER_URI&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;http://localhost.localdomain:11311/
setting /run_id to 1db9f4c6-e044-11e7-9931-b827eba43643
process&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;rosout-1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: started with pid &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;5000&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
started core service &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;/rosout&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Go ahead and quit that by pressing &lt;code&gt;CTRL+C&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;step-2-get-to-know-ros&#34;&gt;Step 2: Get to know ROS&lt;/h2&gt;
&lt;p&gt;One of the reasons I like ROS so much (and, I think, one of the reasons it&amp;rsquo;s so popular) is that their introductory documentation is fantastic. They have a &lt;a href=&#34;https://wiki.ros.org/ROS/Tutorials&#34;&gt;phenomenal set of tutorials&lt;/a&gt; that take you from knowing absolutely nothing to feeling more or less comfortable with the entire system. Each one is easily digestible in a few minutes. Because they are so good, rather than try and duplicate their hard work here, you should just start at the beginning and go through them at least until you complete &lt;em&gt;#13, &amp;ldquo;Examining the Simple Publisher and Subscriber&amp;rdquo;&lt;/em&gt;. Note that there are two parallel tutorial tracks, one that uses C++ and one that uses Python. We&amp;rsquo;ll be using Python through this series, so you don&amp;rsquo;t need to worry about the C++ ones unless they interest you.&lt;/p&gt;
&lt;h2 id=&#34;step-3-setup-python-2&#34;&gt;Step 3: Setup Python 2&lt;/h2&gt;
&lt;p&gt;Now that we&amp;rsquo;ve gained some familiarity with ROS, it&amp;rsquo;s almost time to make our robot move using it. However, there&amp;rsquo;s something we need to do first. Back in CamJam worksheet #1, they mention the following:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;When the Raspberry Pi was first released, some of the important Python libraries were only available for Python 2.7. However, almost every library, and all the ones used in these worksheets, are available for Python 3.2. It has been decided that all code for this EduKit will be developed for Python 3.2.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;~ CamJam worksheet #1&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That&amp;rsquo;s all fine and dandy, and I agree with it, but unfortunately the Python bindings for ROS are only officially supported on Python 2, so we need to use Python 2 from now on instead of Python 3. Don&amp;rsquo;t worry, all the code from the worksheets should still work, but it means we need to install the Python 2 version of RPi.GPIO (we only have the Python 3 version right now):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt install python-dev python-pip python-setuptools
$ pip install RPi.GPIO
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;step-4-create-ros-package-for-our-robot&#34;&gt;Step 4: Create ROS package for our robot&lt;/h2&gt;
&lt;p&gt;Alright, let&amp;rsquo;s have some fun! We&amp;rsquo;re going to rewrite the code we wrote for the CamJam Worksheet #7 using ROS. We&amp;rsquo;ll add some message handling to it, so, using ROS, we can command the robot to move forward, turn left, turn right, etc.&lt;/p&gt;
&lt;p&gt;The first step is to create a new workspace. You learned how to do this in the first ROS tutorial. I&amp;rsquo;m calling mine &amp;ldquo;edukit_bot_ws&amp;rdquo;, if you call yours something else remember to change the directions accordingly:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ mkdir -p ~/edukit_bot_ws/src
$ cd ~/edukit_bot_ws/src
$ catkin_init_workspace
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now let&amp;rsquo;s create a new package in that workspace. I&amp;rsquo;ll call mine &amp;ldquo;edukit_bot&amp;rdquo;, and it has three dependencies: &lt;code&gt;rospy&lt;/code&gt; (the Python bindings for ROS), &lt;code&gt;std_msgs&lt;/code&gt; (the standard ROS messages, e.g. numbers, strings, etc.), and &lt;code&gt;python-rpi.gpio&lt;/code&gt; (RPi.GPIO, which we use for GPIO access):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/edukit_bot_ws/src
$ catkin_create_pkg edukit_bot rospy std_msgs -s python-rpi.gpio
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;step-5-write-the-ros-node&#34;&gt;Step 5: Write the ROS node&lt;/h2&gt;
&lt;p&gt;Time to write some code. First, create a new Python script in the ROS package&amp;rsquo;s &lt;code&gt;src/&lt;/code&gt; directory:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ touch ~/edukit_bot_ws/src/edukit_bot/src/driver_node
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The CamJam worksheets don&amp;rsquo;t discuss this, but if we set the script to be executable, we can run it directly instead of calling it like &lt;code&gt;python path/to/script.py&lt;/code&gt;. Let&amp;rsquo;s do that:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ chmod a+x ~/edukit_bot_ws/src/edukit_bot/src/driver_node
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Open that script in a text editor, and make it look like this (note that the entire package used in this post is &lt;a href=&#34;https://github.com/kyrofa/your-first-robot/tree/master/part_2/edukit_bot&#34;&gt;available for reference&lt;/a&gt;):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/env python&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; rospy
&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; std_msgs.msg &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; String

&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; RPi.GPIO &lt;span style=&#34;color:#f92672&#34;&gt;as&lt;/span&gt; GPIO

&lt;span style=&#34;color:#75715e&#34;&gt;# Set the GPIO modes&lt;/span&gt;
GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;setmode(GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;BCM)
GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;setwarnings(False)

&lt;span style=&#34;color:#75715e&#34;&gt;# Set variables for the GPIO motor pins&lt;/span&gt;
pinMotorAForwards &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;
pinMotorABackwards &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;
pinMotorBForwards &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;
pinMotorBBackwards &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;

&lt;span style=&#34;color:#75715e&#34;&gt;# How many times to turn the pin on and off each second&lt;/span&gt;
Frequency &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# How long the pin stays on each cycle, as a percent (here, it&amp;#39;s 30%)&lt;/span&gt;
DutyCycle &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# Setting the duty cycle to 0 means the motors will not turn&lt;/span&gt;
Stop &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;

&lt;span style=&#34;color:#75715e&#34;&gt;# Set the GPIO Pin mode to be Output&lt;/span&gt;
GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;setup(pinMotorAForwards, GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;OUT)
GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;setup(pinMotorABackwards, GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;OUT)
GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;setup(pinMotorBForwards, GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;OUT)
GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;setup(pinMotorBBackwards, GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;OUT)

&lt;span style=&#34;color:#75715e&#34;&gt;# Set the GPIO to software PWM at &amp;#39;Frequency&amp;#39; Hertz&lt;/span&gt;
pwmMotorAForwards &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;PWM(pinMotorAForwards, Frequency)
pwmMotorABackwards &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;PWM(pinMotorABackwards, Frequency)
pwmMotorBForwards &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;PWM(pinMotorBForwards, Frequency)
pwmMotorBBackwards &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;PWM(pinMotorBBackwards, Frequency)

&lt;span style=&#34;color:#75715e&#34;&gt;# Start the software PWM with a duty cycle of 0 (i.e. not moving)&lt;/span&gt;
pwmMotorAForwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start(Stop)
pwmMotorABackwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start(Stop)
pwmMotorBForwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start(Stop)
pwmMotorBBackwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start(Stop)

&lt;span style=&#34;color:#75715e&#34;&gt;# Turn all motors off&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;StopMotors&lt;/span&gt;():
   pwmMotorAForwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(Stop)
   pwmMotorABackwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(Stop)
   pwmMotorBForwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(Stop)
   pwmMotorBBackwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(Stop)

&lt;span style=&#34;color:#75715e&#34;&gt;# Turn both motors forwards&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Forwards&lt;/span&gt;():
   pwmMotorAForwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(DutyCycle)
   pwmMotorABackwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(Stop)
   pwmMotorBForwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(DutyCycle)
   pwmMotorBBackwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(Stop)

&lt;span style=&#34;color:#75715e&#34;&gt;# Turn both motors backwards&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Backwards&lt;/span&gt;():
   pwmMotorAForwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(Stop)
   pwmMotorABackwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(DutyCycle)
   pwmMotorBForwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(Stop)
   pwmMotorBBackwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(DutyCycle)

&lt;span style=&#34;color:#75715e&#34;&gt;# Turn left&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Left&lt;/span&gt;():
   pwmMotorAForwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(Stop)
   pwmMotorABackwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(DutyCycle)
   pwmMotorBForwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(DutyCycle)
   pwmMotorBBackwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(Stop)

&lt;span style=&#34;color:#75715e&#34;&gt;# Turn Right&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Right&lt;/span&gt;():
   pwmMotorAForwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(DutyCycle)
   pwmMotorABackwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(Stop)
   pwmMotorBForwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(Stop)
   pwmMotorBBackwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(DutyCycle)

&lt;span style=&#34;color:#75715e&#34;&gt;# Message handler&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;CommandCallback&lt;/span&gt;(commandMessage):
   command &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; commandMessage&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;data
   &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; command &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;forwards&amp;#39;&lt;/span&gt;:
       &lt;span style=&#34;color:#66d9ef&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Moving forwards&amp;#39;&lt;/span&gt;)
       Forwards()
   &lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; command &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;backwards&amp;#39;&lt;/span&gt;:
       &lt;span style=&#34;color:#66d9ef&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Moving backwards&amp;#39;&lt;/span&gt;)
       Backwards()
   &lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; command &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;left&amp;#39;&lt;/span&gt;:
       &lt;span style=&#34;color:#66d9ef&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Turning left&amp;#39;&lt;/span&gt;)
       Left()
   &lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; command &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;right&amp;#39;&lt;/span&gt;:
       &lt;span style=&#34;color:#66d9ef&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Turning right&amp;#39;&lt;/span&gt;)
       Right()
   &lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; command &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;stop&amp;#39;&lt;/span&gt;:
       &lt;span style=&#34;color:#66d9ef&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Stopping&amp;#39;&lt;/span&gt;)
       StopMotors()
   &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
       &lt;span style=&#34;color:#66d9ef&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Unknown command, stopping instead&amp;#39;&lt;/span&gt;)
       StopMotors()

rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;init_node(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;driver&amp;#39;&lt;/span&gt;)

rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Subscriber(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;command&amp;#39;&lt;/span&gt;, String, CommandCallback)

rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;spin()
&lt;span style=&#34;color:#66d9ef&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Shutting down: stopping motors&amp;#39;&lt;/span&gt;)
StopMotors()
GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cleanup()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Lots of that should look familiar, but let&amp;rsquo;s break it down into chunks.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/env python&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; rospy
&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; std_msgs.msg &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; String
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The very first line of this file is called a &lt;a href=&#34;https://en.wikipedia.org/wiki/Shebang_(Unix)&#34;&gt;shebang&lt;/a&gt;. Since we&amp;rsquo;ve marked this file as executable on its own, this defines the interpreter that will execute this program. In this case, we&amp;rsquo;re telling it that it needs the &lt;code&gt;python&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;We then import &lt;code&gt;rospy&lt;/code&gt;, which includes the ROS Python bindings, and we import the &lt;code&gt;String&lt;/code&gt; message from the ROS &lt;code&gt;std_msgs&lt;/code&gt;. We&amp;rsquo;ll use both of these a little later in the program.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; RPi.GPIO &lt;span style=&#34;color:#f92672&#34;&gt;as&lt;/span&gt; GPIO

&lt;span style=&#34;color:#75715e&#34;&gt;# Set the GPIO modes&lt;/span&gt;
GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;setmode(GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;BCM)
GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;setwarnings(False)

&lt;span style=&#34;color:#75715e&#34;&gt;# &amp;lt;snip you don&amp;#39;t need to see all this again...&amp;gt;&lt;/span&gt;

&lt;span style=&#34;color:#75715e&#34;&gt;# Turn Right&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Right&lt;/span&gt;():
   pwmMotorAForwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(DutyCycle)
   pwmMotorABackwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(Stop)
   pwmMotorBForwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(Stop)
   pwmMotorBBackwards&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ChangeDutyCycle(DutyCycle)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This entire section was lifted virtually verbatim from the CamJam Worksheet #7. It&amp;rsquo;s explained there, so I won&amp;rsquo;t repeat the explanation here.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Message handler&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;CommandCallback&lt;/span&gt;(commandMessage):
   command &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; commandMessage&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;data
   &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; command &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;forwards&amp;#39;&lt;/span&gt;:
       &lt;span style=&#34;color:#66d9ef&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Moving forwards&amp;#39;&lt;/span&gt;)
       Forwards()
   &lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; command &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;backwards&amp;#39;&lt;/span&gt;:
       &lt;span style=&#34;color:#66d9ef&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Moving backwards&amp;#39;&lt;/span&gt;)
       Backwards()
   &lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; command &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;left&amp;#39;&lt;/span&gt;:
       &lt;span style=&#34;color:#66d9ef&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Turning left&amp;#39;&lt;/span&gt;)
       Left()
   &lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; command &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;right&amp;#39;&lt;/span&gt;:
       &lt;span style=&#34;color:#66d9ef&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Turning right&amp;#39;&lt;/span&gt;)
       Right()
   &lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; command &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;stop&amp;#39;&lt;/span&gt;:
       &lt;span style=&#34;color:#66d9ef&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Stopping&amp;#39;&lt;/span&gt;)
       StopMotors()
   &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
       &lt;span style=&#34;color:#66d9ef&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Unknown command, stopping instead&amp;#39;&lt;/span&gt;)
       StopMotors()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s a new, ROS-specific part. The &lt;code&gt;CommandCallback&lt;/code&gt; function is created to handle a &lt;code&gt;String&lt;/code&gt; message. It simply takes a look at the data (i.e. the string itself) contained within the message, and takes action appropriately. For example, if the string is the word &amp;ldquo;forwards&amp;rdquo; it moves the robot forward by calling the &lt;code&gt;Forwards&lt;/code&gt; function created in the worksheet. Similarly, if the word is &amp;ldquo;left&amp;rdquo; it turns the robot left by calling the &lt;code&gt;Left&lt;/code&gt; function. If the command isn&amp;rsquo;t one of the recognized words, the function does the safest thing it can: &lt;em&gt;stop&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;init_node(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;driver&amp;#39;&lt;/span&gt;)

rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Subscriber(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;command&amp;#39;&lt;/span&gt;, String, CommandCallback)

rospy&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;spin()
&lt;span style=&#34;color:#66d9ef&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Shutting down: stopping motors&amp;#39;&lt;/span&gt;)
StopMotors()
GPIO&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cleanup()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s the main part of the program. First of all, we initialize the node and give it a name (&amp;ldquo;driver&amp;rdquo;). This begins communication with the ROS master. We then subscribe to a topic named &amp;ldquo;command&amp;rdquo;, and we specify that we expect that topic to be a &lt;code&gt;String&lt;/code&gt; message. We then provide our &lt;code&gt;CommandCallback&lt;/code&gt; function to request that it be called when a new messages comes in on that topic.&lt;/p&gt;
&lt;p&gt;Then we call &lt;code&gt;rospy.spin()&lt;/code&gt; which blocks and waits for messages to come in. Once the node is asked to quit (say, with a &lt;code&gt;CTRL+C&lt;/code&gt;), that function will exit, at which time we ensure that the motors have been stopped. We don&amp;rsquo;t want the robot running away from us!&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re done with our workspace for now, so let&amp;rsquo;s build it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/edukit_bot_ws
$ catkin_make
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;step-6-move-the-robot-using-ros&#34;&gt;Step 6: Move the robot using ROS&lt;/h2&gt;
&lt;p&gt;At this point, we have a ROS node created that will drive our robot as requested in a &amp;ldquo;command&amp;rdquo; message. It uses GPIO though, which still requires &lt;code&gt;sudo&lt;/code&gt;. Instead of trying to get our workspace working using &lt;code&gt;sudo&lt;/code&gt;, let&amp;rsquo;s temporarily change the permissions of GPIO so that we don&amp;rsquo;t need &lt;code&gt;sudo&lt;/code&gt; (this will be reset upon reboot):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo chmod a+rw /dev/gpiomem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;/dev/gpiomem&lt;/code&gt; is a device representing the memory dedicated to GPIO (i.e. not other, more important/dangerous memory). As a result, this operation is relatively safe, particularly compared to doing the same to e.g. &lt;code&gt;/dev/mem&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Alright, let&amp;rsquo;s test this out! You&amp;rsquo;ll need to open three terminals for this, each one using the classic shell (remember, run &lt;code&gt;sudo classic&lt;/code&gt; to enter the classic shell). We first need the ROS master, as without it publishers and subscribers can&amp;rsquo;t find one another. So in one terminal, run the ROS master:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ roscore
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In another terminal, make sure you activate our newly-built workspace, and run our &amp;ldquo;driver&amp;rdquo; node:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/edukit_bot_ws
$ source devel/setup.sh
$ rosrun edukit_bot driver_node
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, in the third terminal, we&amp;rsquo;ll start giving our commands to make the robot move. First of all, take note of the topics that either have publishers or subscribers:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ rostopic list
/command
/rosout
/rosout_agg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice the &lt;code&gt;/command&lt;/code&gt; topic. This is the topic on which our &amp;ldquo;driver&amp;rdquo; node is listening, because of the &lt;code&gt;Subscriber&lt;/code&gt; we configured. It&amp;rsquo;s expecting a &lt;code&gt;String&lt;/code&gt; message there, so let&amp;rsquo;s send it a command, say, to move forward:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ rostopic pub -1 /command std_msgs/String &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;forwards&amp;#34;&lt;/span&gt;
publishing and latching message &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; 3.0 seconds
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You should notice the &amp;ldquo;driver&amp;rdquo; node say that it&amp;rsquo;s moving forward, and then your robot&amp;rsquo;s wheels should start rotating forward! Try sending any of the strings we handled in &lt;code&gt;CommandCallback&lt;/code&gt; (&amp;ldquo;left&amp;rdquo;, &amp;ldquo;backwards&amp;rdquo;, etc.), and commands that you know are invalid to ensure that it stops safely.&lt;/p&gt;
&lt;p&gt;Congratulations, you&amp;rsquo;re quickly learning ROS! In the &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-the-controller-3-5&#34;&gt;next post in this series&lt;/a&gt;, we&amp;rsquo;ll break free of the CamJam worksheets and strike out on our own. We&amp;rsquo;ll introduce the wireless controller, and begin working on making our robot remotely-controlled using ROS.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Your first robot: A beginner&#39;s guide to ROS and Ubuntu Core [1/5]</title>
      <link>https://kyrofa.com/posts/your-first-robot-a-beginner-s-guide-to-ros-and-ubuntu-core-1-5/</link>
      <pubDate>Sun, 17 Dec 2017 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/your-first-robot-a-beginner-s-guide-to-ros-and-ubuntu-core-1-5/</guid>
      <description>UPDATE: I&amp;rsquo;m leaving this series up for historical purposes, but please note that I no longer recommend Ubuntu Core or snaps for use in robotics.
Some time ago I created a blog/video series that walked the reader through creating a prototype using the Robot Operating System (ROS) and taking it to production using Ubuntu Core. However, that series was intended more for robotics professionals; it assumed quite a bit of ROS knowledge, and required some costly equipment (the robot was about $1k).</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;UPDATE: I&amp;rsquo;m leaving this series up for historical purposes, but please note that I no longer recommend Ubuntu Core or snaps for use in robotics.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Some time ago I created a &lt;a href=&#34;https://kyrofa.com/posts/from-ros-prototype-to-production-on-ubuntu-core-1-5&#34;&gt;blog/video series&lt;/a&gt; that walked the reader through creating a prototype using the Robot Operating System (ROS) and taking it to production using Ubuntu Core. However, that series was intended more for robotics professionals; it assumed quite a bit of ROS knowledge, and required some costly equipment (the robot was about $1k). Well, Ubuntu is also for hobbyists (and kids!) who don&amp;rsquo;t want to shell out $1k to play with robots. Thus, this series was born: one that doesn&amp;rsquo;t assume any ROS knowledge, and uses hardware that&amp;rsquo;s so inexpensive you could give it as a Christmas gift. I present you with a robot that costs less than $100: The &lt;a href=&#34;https://thepihut.com/collections/camjam-edukit/products/camjam-edukit-3-robotics&#34;&gt;CamJam EduKit #3&lt;/a&gt;, which is a wheeled robot kit you assemble and control with a Raspberry Pi. Let me break down the entire list of hardware you&amp;rsquo;ll need to follow this series:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The EduKit, £18, so, say, $25&lt;/li&gt;
&lt;li&gt;A Raspberry Pi (I&amp;rsquo;m using a &lt;a href=&#34;https://www.raspberrypi.org/products/raspberry-pi-2-model-b/&#34;&gt;Raspberry Pi 2&lt;/a&gt;, you need either the 2 or 3), $35&lt;/li&gt;
&lt;li&gt;Battery for Raspberry Pi (I used &lt;a href=&#34;https://www.amazon.com/gp/product/B00MWV1TJ6/ref=od_aui_detailpages00?ie=UTF8&amp;amp;psc=1&#34;&gt;this one&lt;/a&gt;), $15
&lt;ul&gt;
&lt;li&gt;This is optional, but if you don&amp;rsquo;t have one your Pi will be tethered by its power supply.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://thepihut.com/collections/raspberry-pi-gaming/products/raspberry-pi-compatible-wireless-gamepad-controller?variant=38135423121&#34;&gt;Wireless controller&lt;/a&gt;, £14, so, say, $20&lt;/li&gt;
&lt;li&gt;Total, including optional components: &lt;strong&gt;$95&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In my previous robotics series, one of the reasons the Turtlebot was so expensive is because it has a plethora of software already written for it, like ROS drivers to control its movement, read from its sensors, etc. This little robot is different: we&amp;rsquo;ll need to write everything ourselves. Thankfully, the CamJam folks provide a &lt;a href=&#34;http://camjam.me/?page_id=1035&#34;&gt;number of worksheets&lt;/a&gt; meant to introduce you to the platform and get you started writing software to control the wheels, read from the ultrasonic sensor, and so on. We can leverage those to get up and running quickly, so throughout this series we&amp;rsquo;ll cover the following topics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-introduction-to-the-robot-operating-system-2-5&#34;&gt;Your first robot: Introduction to the Robot Operating System [2/5]&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;What is the Robot Operating System (ROS)? Why is it worth learning? How do I use it?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-the-controller-3-5&#34;&gt;Your first robot: The controller [3/5]&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Getting data out of the wireless controller and into ROS.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-the-driver-4-5&#34;&gt;Your first robot: The driver [4/5]&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Learn a little control theory, write the driver for the robot.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-sharing-with-others-5-5&#34;&gt;Your first robot: Sharing with others [5/5]&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;How do you start the system without needing to SSH in? How do you share your final product with your friends?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As I mentioned, the CamJam worksheets will get us a long way toward what we need to learn for this series. However, rather than use Raspbian, we&amp;rsquo;re going to use Ubuntu Core. The worksheets are still perfectly applicable, and I&amp;rsquo;ll prove it by walking you through getting Ubuntu Core setup on your Raspberry Pi, and then we&amp;rsquo;ll go through one of the CamJam worksheets. Note that this is also a video series, feel free to watch the video version of this post:&lt;/p&gt;

&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
  &lt;iframe src=&#34;https://www.youtube-nocookie.com/embed/KidVVqbsIHI&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; allowfullscreen title=&#34;YouTube Video&#34;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;While this is meant to be an introduction, I want to be clear on some of the assumptions I&amp;rsquo;m making. This series assumes you&amp;rsquo;re at least somewhat familiar with Ubuntu (or other Debian-based distro, like Raspbian), particularly using the command-line. I don&amp;rsquo;t expect you to be a shell-scripting pro, but you should know how to navigate around the file system and use terminal-based editors (&lt;code&gt;vi&lt;/code&gt;, &lt;code&gt;nano&lt;/code&gt;, etc.)&lt;/p&gt;
&lt;h2 id=&#34;what-is-ubuntu-core&#34;&gt;What is Ubuntu Core?&lt;/h2&gt;
&lt;p&gt;Ubuntu Core is a specialized distribution of Ubuntu that is dedicated to devices that fit into the &amp;ldquo;Internet of Things&amp;rdquo; (IoT) bucket. This includes routers, thermostats, and of course, robots. It&amp;rsquo;s my hope that, in introducing you to the operating system (and tools like ROS) that the pros use for their robots, the road is paved for you to go on and accomplish great things in this field!&lt;/p&gt;
&lt;p&gt;Ubuntu Core is a bit different from Raspbian. It&amp;rsquo;s even a bit different than classic Ubuntu (or Ubuntu MATE)! Let&amp;rsquo;s get started installing it, and I&amp;rsquo;ll explain what I mean.&lt;/p&gt;
&lt;h2 id=&#34;step-1-install-ubuntu-core&#34;&gt;Step 1: Install Ubuntu Core&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I no longer recommend using Ubuntu Core to learn this stuff. It was used initially here because it was the only official Ubuntu image that ran on the Pi, but that&amp;rsquo;s &lt;a href=&#34;https://ubuntu.com/download/raspberry-pi&#34;&gt;no longer the case&lt;/a&gt;. You can install Ubuntu Server or even the full-blown Ubuntu Desktop, and still follow all the snap-related steps outlined in these tutorials without needing to deal with the somewhat-foreign Ubuntu Core.&lt;/p&gt;
&lt;p&gt;This is already well-documented, but I want to add a few notes here before you follow the steps. First of all, a keyboard and monitor are required, but only for the first boot. Unlike Raspbian or Ubuntu MATE, there is no default username/password or install process where you create a user. It doesn&amp;rsquo;t even use a mouse&amp;ndash; this is more like Ubuntu Server. In fact, by default there&amp;rsquo;s no local login at all: it&amp;rsquo;s all done over SSH (a remote login over the network), with cryptographic keys instead of passwords. This is part of Ubuntu Core&amp;rsquo;s &amp;ldquo;secure by default&amp;rdquo; philosophy. There are numerous examples of default credentials being abused (see the &lt;a href=&#34;https://en.wikipedia.org/wiki/Mirai_(malware)&#34;&gt;Mirai botnet&lt;/a&gt;), and as an IoT-focused operating system, Ubuntu Core can&amp;rsquo;t have that. That&amp;rsquo;s why you&amp;rsquo;ll see an Ubuntu SSO account and SSH keys as prerequisites in the install guide (don&amp;rsquo;t worry, both are easy).&lt;/p&gt;
&lt;p&gt;Alright, go ahead and follow the &lt;a href=&#34;https://developer.ubuntu.com/core/get-started/raspberry-pi-2-3&#34;&gt;Raspberry Pi 2 or 3 install guide&lt;/a&gt; (make a new topic &lt;a href=&#34;https://forum.snapcraft.io/&#34;&gt;on the forum&lt;/a&gt; if you have any questions). At the end of this step, you should have Ubuntu Core installed, and you should be able to SSH into the Pi.&lt;/p&gt;
&lt;h2 id=&#34;step-2-prepare-developer-setup&#34;&gt;Step 2: Prepare developer setup&lt;/h2&gt;
&lt;p&gt;As an IoT-focused operating system, Ubuntu Core uses a different packaging format from Raspbian and classic Ubuntu. Rather than using Debian packaging (which involves familiar tools like &lt;code&gt;apt&lt;/code&gt;), Ubuntu Core uses a packaging format called &lt;code&gt;snaps&lt;/code&gt;. Snaps are far more robust when it comes to reliable updates and security, both of which are important to IoT devices, including robots. There is no &lt;code&gt;apt&lt;/code&gt; on this system. To give you a taste of what using the &lt;code&gt;snap&lt;/code&gt; command is like, ensure your system is completely up-to-date by running &lt;code&gt;snap refresh&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So how does one install all the developer tools to which one is accustomed? There&amp;rsquo;s a special snap for that called &lt;code&gt;classic&lt;/code&gt;, which gives you access to all your familiar tools (including &lt;code&gt;apt&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Install the &lt;code&gt;classic&lt;/code&gt; snap by following the &amp;ldquo;developing on target&amp;rdquo; section of the &lt;a href=&#34;https://developer.ubuntu.com/core/get-started/developer-setup&#34;&gt;Developer setup guide&lt;/a&gt; (again, create a new forum topic if you have any questions). At the end of this step, you should be able to run &lt;code&gt;sudo classic&lt;/code&gt; and gain access to a shell where you can install debs, which we&amp;rsquo;ll use for hacking on our robot! Later we&amp;rsquo;ll walk through creating your own snap to control the robot, which makes sharing your project with your friends super easy.&lt;/p&gt;
&lt;h2 id=&#34;step-3-camjam-time&#34;&gt;Step 3: CamJam time&lt;/h2&gt;
&lt;p&gt;Alright, we&amp;rsquo;re now to the point where we can begin following CamJam&amp;rsquo;s worksheets! Why don&amp;rsquo;t we start with the &lt;a href=&#34;https://github.com/CamJam-EduKit/EduKit3/raw/f8e7e2d154b23538f2f219a170e8011b76812479/CamJam%20EduKit%203%20-%20Robotics%20Worksheet%201%20-%20Introduction.pdf&#34;&gt;very first one&lt;/a&gt;. This one really assumes you&amp;rsquo;re running Raspbian, which we&amp;rsquo;re not, so while you can read the whole thing, we&amp;rsquo;re going to skip straight to the &amp;ldquo;Identifying your Version of Raspbian&amp;rdquo; step. No, we&amp;rsquo;re still not using Raspbian, but this section makes an interesting point that I want to make sure you notice.&lt;/p&gt;
&lt;p&gt;Ubuntu Core, like Debian Wheezy, considers GPIO access via memory mapping to be a privileged operation. As a result, any of the code throughout these worksheets that uses GPIO will need to be run with &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Alright, let&amp;rsquo;s get started. Make sure you&amp;rsquo;re in your classic shell by running &lt;code&gt;sudo classic&lt;/code&gt;. &lt;code&gt;vi&lt;/code&gt; is available here by default, but you can &lt;code&gt;sudo apt install nano&lt;/code&gt; if you want. Following the worksheet, open up &lt;code&gt;1-helloworld.py&lt;/code&gt; and fill it with the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Print Hello World!&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello World!&amp;#39;&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then save and exit. You can now run the code in that file with this command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ python3 1-helloworld.py
Hello World!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Go ahead and read through the rest of the worksheet, it will be helpful to you later on.&lt;/p&gt;
&lt;p&gt;I hope this gets you off to a good start using Ubuntu Core to hack on your Raspberry Pi projects. As homework for the rest of the series, also complete worksheets 2-4, and 7. You can totally do the other worksheets as well, but we won&amp;rsquo;t be using the ultrasonic sensor or line detector in this series, so you don&amp;rsquo;t have to. Before you do any other worksheets, though, you need to get the RPi.GPIO package installed. While in your classic shell, run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt install gcc python3-dev python3-pip python3-setuptools
$ pip3 install RPi.GPIO
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Also, remember that you&amp;rsquo;ll need to use &lt;code&gt;sudo&lt;/code&gt; for any python scripts that utilize GPIO. The &lt;a href=&#34;https://kyrofa.com/posts/your-first-robot-introduction-to-the-robot-operating-system-2-5&#34;&gt;next post in this series&lt;/a&gt; will be an introduction to ROS, what it is, why it&amp;rsquo;s useful, why you need to know it, and how to get it on your Ubuntu-Core-powered Raspberry Pi.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Google Code-in 2017</title>
      <link>https://kyrofa.com/posts/google-code-in-2017/</link>
      <pubDate>Mon, 27 Nov 2017 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/google-code-in-2017/</guid>
      <description>Tomorrow, it begins. The culmination of weeks of research, documentation, and preparation by multiple open-source projects, just waiting to start hacking away with students. Tomorrow, students aged 13-17 can begin participating in Google Code-in 2017.
Ubuntu is one of the mentoring organizations, with several of my colleagues and me as mentors. Just in case you haven&amp;rsquo;t heard of it, Ubuntu is an open-source operating system for servers, desktops, and embedded devices.</description>
      <content:encoded>&lt;p&gt;Tomorrow, it begins. The culmination of weeks of research, documentation, and preparation by multiple open-source projects, just waiting to start hacking away with students. Tomorrow, students aged 13-17 can begin participating in &lt;a href=&#34;https://codein.withgoogle.com/&#34;&gt;Google Code-in 2017&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.ubuntu.com/&#34;&gt;Ubuntu&lt;/a&gt; is one of the mentoring organizations, with several of my colleagues and me as mentors. Just in case you haven&amp;rsquo;t heard of it, Ubuntu is an open-source operating system for servers, desktops, and embedded devices. Even those who have never heard of it &lt;a href=&#34;http://blog.dustinkirkland.com/2015/12/more-people-use-ubuntu-than-anyone.html&#34;&gt;use it daily&lt;/a&gt;: Paypal, Netflix, Wikipedia, Pinterest, Reddit, Snapchat, Spotify&amp;ndash; all these run on Ubuntu. It&amp;rsquo;s an awesome project, and very rewarding to work on!&lt;/p&gt;
&lt;p&gt;One of the best ways to get involved in computer science is with open-source software (like Ubuntu). One of the best ways to get involved in open-source software is with mentorship, which is exactly what Google Code-in provides&amp;ndash; with prizes, including a trip to Google&amp;rsquo;s headquarters! Tasks range from creating documentation (tutorials, etc.) to translations, all the way to hard-core coding tasks. There&amp;rsquo;s something there for everyone, and the product of your work will be used by millions of people!&lt;/p&gt;
&lt;p&gt;If you fall in this age range, I know I can speak for all my colleagues when I say that we&amp;rsquo;re looking forward to working with you. If you&amp;rsquo;re local, feel free to reach out by commenting below and hitting the &amp;ldquo;notify me of other comments&amp;rdquo; checkbox and entering your email address (which will be kept private). If there&amp;rsquo;s interest, perhaps we can put a workshop together.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Nextcloud snap to be updated to v12</title>
      <link>https://kyrofa.com/posts/nextcloud-snap-to-be-updated-to-v12/</link>
      <pubDate>Mon, 20 Nov 2017 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/nextcloud-snap-to-be-updated-to-v12/</guid>
      <description>If you&amp;rsquo;re a user of the Nextcloud snap, you may have noticed that upstream Nextcloud released v12 a while back, but you&amp;rsquo;re still on v11. That&amp;rsquo;s because v12 introduced several regressions that made us maintainers decide to hold off on the upgrade. Thankfully, things are finally looking up.
As of today, it seems that all the blocking regressions have been fixed. Which means that, barring any additional regressions being introduced between now and then, v12.</description>
      <content:encoded>&lt;p&gt;If you&amp;rsquo;re a user of the Nextcloud snap, you may have noticed that upstream Nextcloud released v12 a while back, but you&amp;rsquo;re still on v11. That&amp;rsquo;s because v12 introduced &lt;a href=&#34;https://github.com/nextcloud/nextcloud-snap/issues/280&#34;&gt;several regressions&lt;/a&gt; that made us maintainers decide to hold off on the upgrade. Thankfully, things are finally looking up.&lt;/p&gt;
&lt;p&gt;As of today, it seems that all the blocking regressions have been fixed. Which means that, barring any additional regressions being introduced between now and then, v12.0.4 will be the release that sees the Nextcloud snap join the party.&lt;/p&gt;
&lt;p&gt;What does this mean for you, oh treasured Nextcloud snap user? If you, like the majority of Nextcloud users, &lt;em&gt;want&lt;/em&gt; to be on v12, it means nothing. Just wait for it to automatically update. However, what if you &lt;em&gt;don&amp;rsquo;t&lt;/em&gt; want v12? Maybe an app you&amp;rsquo;re using hasn&amp;rsquo;t been updated yet, or you just really like v11? Then this post is for you.&lt;/p&gt;
&lt;p&gt;The snap is &lt;a href=&#34;https://github.com/nextcloud/nextcloud-snap/wiki/Release-strategy&#34;&gt;broken down into tracks&lt;/a&gt;, of which there are currently three: &lt;code&gt;11&lt;/code&gt;, &lt;code&gt;12&lt;/code&gt;, and &lt;code&gt;latest&lt;/code&gt;. Each track is composed of four channels, not all of which are utilized by the Nextcloud snap, but I&amp;rsquo;ll outline their usages here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;11&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;edge&lt;/code&gt;: Daily builds of Nextcloud&amp;rsquo;s stable11 branch (i.e. what will be coming down the pipe in the next v11 point release).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;beta&lt;/code&gt;/&lt;code&gt;candidate&lt;/code&gt;: Not used&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stable&lt;/code&gt;: The latest stable release of Nextcloud v11. Will never update to v12.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;12&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;edge&lt;/code&gt;: Daily builds of Nextcloud&amp;rsquo;s stable12 branch (i.e. what will be coming down the pipe in the next v12 point release).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;beta&lt;/code&gt;/&lt;code&gt;candidate&lt;/code&gt;/&lt;code&gt;stable&lt;/code&gt;: As of now, nothing. As soon as the snap is based on v12, the stable channel will contain the latest stable release of Nextcloud v12. Will never update to v13.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;latest&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;edge&lt;/code&gt;: Daily builds of Nextcloud&amp;rsquo;s master branch (look where development is heading)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;beta&lt;/code&gt;: The development branch of the snap itself&lt;/li&gt;
&lt;li&gt;&lt;code&gt;candidate&lt;/code&gt;: Used for release candidates and calls for testing&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stable&lt;/code&gt;: The latest stable release of Nextcloud.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So by default, when you &lt;code&gt;snap install nextcloud&lt;/code&gt;, you&amp;rsquo;re on the &lt;code&gt;stable&lt;/code&gt; channel of the &lt;code&gt;latest&lt;/code&gt; track (denoted as &lt;code&gt;latest/stable&lt;/code&gt;). This means that, by default, you&amp;rsquo;ll upgrade automatically from v11 to v12 when it comes out. If you don&amp;rsquo;t want that, and would prefer to stay on v11 (including its point releases), you need to be using the &lt;code&gt;stable&lt;/code&gt; channel of the &lt;code&gt;11&lt;/code&gt; track. To switch to that channel, run this command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap refresh nextcloud --channel&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;11/stable
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As soon as you run that command, you&amp;rsquo;ll begin tracking &lt;code&gt;11/stable&lt;/code&gt;, and will only update when new v11 point releases come out. Then, if/when you&amp;rsquo;re ready, you can switch channels again to upgrade to v12:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap refresh nextcloud --channel&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;12/stable
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you&amp;rsquo;ll only update when new v12 point releases come out. If you want to jump back to getting the newest stable release (including v13 when it comes out) refresh back to &lt;code&gt;latest/stable&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap refresh nextcloud --stable
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s important to note that snaps allow changing channels at any time, from any channel to any other channel. That includes, say, changing from &lt;code&gt;12/stable&lt;/code&gt; to &lt;code&gt;11/stable&lt;/code&gt;. While this is technically possible, Nextcloud does not support this. Downgrading is not supported and will be met with a significant lack of happiness.&lt;/p&gt;
&lt;p&gt;Anyway, I just wanted to give you a heads up that this will be happening soon, so you can take action if necessary.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Ubuntu 17.10 Release Party</title>
      <link>https://kyrofa.com/posts/ubuntu-17-10-release-party/</link>
      <pubDate>Thu, 19 Oct 2017 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/ubuntu-17-10-release-party/</guid>
      <description>Were you busy today? Did you forget that Ubuntu 17.10 was released today (no way)? Perhaps you agree that Thursdays are lame for a party anyway? Walla Walla University&amp;rsquo;s ACM club is having their kick-off event, a joint retro video game and Ubuntu 17.10 release party, on Sunday, October 22nd from 3pm to 5pm!
This event will be taking place in room 348 of Kretschmar Hall on the Walla Walla University campus in Walla Walla, WA.</description>
      <content:encoded>&lt;p&gt;Were you busy today? Did you forget that Ubuntu 17.10 was released today (no way)? Perhaps you agree that Thursdays are lame for a party anyway? Walla Walla University&amp;rsquo;s ACM club is having their kick-off event, a joint retro video game and Ubuntu 17.10 release party, on Sunday, October 22nd from 3pm to 5pm!&lt;/p&gt;
&lt;p&gt;This event will be taking place in room 348 of &lt;a href=&#34;https://mywwu.wallawalla.edu/directory/buildings/krh&#34;&gt;Kretschmar Hall&lt;/a&gt; on the Walla Walla University campus in Walla Walla, WA. The schedule looks something like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Ubuntu presentation (me. You can use this time to go through the swag I&amp;rsquo;m bringing)&lt;/li&gt;
&lt;li&gt;Social break&lt;/li&gt;
&lt;li&gt;Retro video games/installation support/networking/general hanging out&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you happen to be in the area, I&amp;rsquo;d love to see you there!&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Snap configuration: the configure hook</title>
      <link>https://kyrofa.com/posts/snap-configuration-the-configure-hook/</link>
      <pubDate>Tue, 12 Sep 2017 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/snap-configuration-the-configure-hook/</guid>
      <description>You&amp;rsquo;ve heard it a million times: snaps bundle their dependencies. People seem to understand and accept the technical aspects of this, but today I want to talk about a more philosophical aspect. If you&amp;rsquo;re used to more traditional packaging, then you&amp;rsquo;re used to each project being standalone, e.g. Apache is its own package, with its own configuration; PHP is its own package, with its own configuration; and so on. As you begin creating a snap, perhaps one that bundles Apache, it&amp;rsquo;s easy to automatically gravitate toward wanting to make those config files available to your users as well.</description>
      <content:encoded>&lt;p&gt;You&amp;rsquo;ve heard it a million times: snaps bundle their dependencies. People seem to understand and accept the technical aspects of this, but today I want to talk about a more philosophical aspect. If you&amp;rsquo;re used to more traditional packaging, then you&amp;rsquo;re used to each project being standalone, e.g. Apache is its own package, with its own configuration; PHP is its own package, with its own configuration; and so on. As you begin creating a snap, perhaps one that bundles Apache, it&amp;rsquo;s easy to automatically gravitate toward wanting to make those config files available to your users as well. That&amp;rsquo;s fine, and doing something like that is covered in an &lt;a href=&#34;https://kyrofa.com/posts/snap-install-time-setup-the-install-hook&#34;&gt;earlier post about the &lt;code&gt;install&lt;/code&gt; hook&lt;/a&gt;, but I want to challenge your thinking.&lt;/p&gt;
&lt;p&gt;Think about what you&amp;rsquo;re creating. Continuing with the Apache example, is it just an Apache snap? Meant to host any web application? If so, then yeah, it probably needs to be super flexible and exposing the full Apache config to the user may be a reasonable thing to do. However, oftentimes you&amp;rsquo;re bundling Apache together with other things to create a specific product, perhaps a web application. If so, I suggest you stop thinking of your snap as a collection of parts (Apache, PHP, MySQL), and think of it as quite simply &lt;em&gt;your product&lt;/em&gt;. What experience do you want your user to have when interacting with it? Does your user even need to care that you use Apache? What if you want to use NGINX later? Do you want to force your user to edit the full Apache config when the only thing they want to do is change the port? Perhaps it would be better to have a slick way for the user to &lt;em&gt;ask your product&lt;/em&gt; to simply listen on a different port. Snapd actually provides a standardized configuration interface that interacts with your snap by way of the &lt;code&gt;configure&lt;/code&gt; hook.&lt;/p&gt;
&lt;p&gt;As a reminder, hooks are simply executables that are shipped in the snap which snapd calls at predefined times. When is the &lt;code&gt;configure&lt;/code&gt; hook called? In a few different situations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When the snap is first installed, after services are started&lt;/li&gt;
&lt;li&gt;When the snap is refreshed (updated)&lt;/li&gt;
&lt;li&gt;Whenever snap set is called to alter the snap&amp;rsquo;s configuration&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(Honestly, I think the first two situations was a mistake, but I digress.)&lt;/p&gt;
&lt;p&gt;Bear with me, now. I want to show you what I mean with a quick tutorial.&lt;/p&gt;
&lt;h2 id=&#34;tutorial&#34;&gt;Tutorial&lt;/h2&gt;
&lt;p&gt;In an &lt;a href=&#34;https://kyrofa.com/posts/snap-install-time-setup-the-install-hook&#34;&gt;earlier post about the install hook&lt;/a&gt;, we created a very simple snap containing a service that said &amp;ldquo;Hello, World!&amp;rdquo; at a rate determined by a configuration file. I&amp;rsquo;d like to build on that example here, by no longer using a configuration file at all, and using snapd to manage the configuration for us.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s start with that snap. If you don&amp;rsquo;t happen to have it around, you don&amp;rsquo;t need to read the other post&amp;ndash; just &lt;a href=&#34;https://github.com/kyrofa/install-hook-tutorial&#34;&gt;grab it off of GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our first step toward letting snapd manage our configuration is to stop using a configuration file. That file is created in the &lt;code&gt;install&lt;/code&gt; hook, so let&amp;rsquo;s start there. Right now it looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/sh -e
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# Create a default config file&lt;/span&gt;
echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sleep_time 5&amp;#34;&lt;/span&gt; &amp;gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$SNAP_DATA&lt;span style=&#34;color:#e6db74&#34;&gt;/hello.conf&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Instead of creating that config file, we can ask snapd to record that configuration for us by using the &lt;code&gt;snapctl&lt;/code&gt; command (see snapctl -h to learn more about it). Change the install hook to look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/sh -e
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# Initialize the sleep time to a default&lt;/span&gt;
snapctl set sleep-time&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Using &lt;code&gt;snapctl&lt;/code&gt; in this way saves the value &lt;code&gt;5&lt;/code&gt; into the &lt;code&gt;sleep-time&lt;/code&gt; variable of this particular snap&amp;rsquo;s configuration (e.g. another snap could use &lt;code&gt;snapctl set sleep-time=2&lt;/code&gt; and this one would still be &lt;code&gt;5&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Now let&amp;rsquo;s change our service to use &lt;code&gt;snapctl&lt;/code&gt; instead of the config file as well:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/sh -e
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; true; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
   &lt;span style=&#34;color:#75715e&#34;&gt;# First, determine our rate by determining how long we should sleep&lt;/span&gt;
   sleep_time&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;snapctl get sleep-time&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;

   &lt;span style=&#34;color:#75715e&#34;&gt;# Now be nice and greet&lt;/span&gt;
   echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello, World!&amp;#34;&lt;/span&gt;

   &lt;span style=&#34;color:#75715e&#34;&gt;# Now sleep for the time requested&lt;/span&gt;
   sleep &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$sleep_time&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At this point we could build and install this snap, and it would behave exactly the same way as in the previous post (printing its greeting every 5 seconds), but we wouldn&amp;rsquo;t be using a configuration file anymore. Of course, that also means that the user has no way to change this behavior, and here I was bragging about this &amp;ldquo;standardized configuration interface&amp;rdquo; blah blah, what about &lt;em&gt;that&lt;/em&gt;, you ask?&lt;/p&gt;
&lt;p&gt;Well, in the same way that &lt;code&gt;snapctl set&lt;/code&gt;/&lt;code&gt;get&lt;/code&gt; can be used from &lt;em&gt;within&lt;/em&gt; the snap, &lt;code&gt;snap get&lt;/code&gt;/&lt;code&gt;set&lt;/code&gt; can be used from &lt;em&gt;outside&lt;/em&gt; the snap (by the user). However, &lt;code&gt;snap set&lt;/code&gt; can only be used once we implement a configure hook, so let&amp;rsquo;s do that.&lt;/p&gt;
&lt;p&gt;First, create the hook and make it executable:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ touch snap/hooks/configure
$ chmod a+x snap/hooks/configure
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now make that file look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/sh -e
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# Obtain sleep-time value&lt;/span&gt;
sleep_time&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;snapctl get sleep-time&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;

&lt;span style=&#34;color:#75715e&#34;&gt;# Validate it&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; ! expr &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$sleep_time&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; : &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;^[0-9]*$&amp;#39;&lt;/span&gt; &amp;gt; /dev/null; &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;
   echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;\&amp;#34;&lt;/span&gt;$sleep_time&lt;span style=&#34;color:#e6db74&#34;&gt;\&amp;#34; is not a valid sleep time&amp;#34;&lt;/span&gt; &amp;gt;&amp;amp;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
   exit &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So here&amp;rsquo;s how this works. When the user calls &lt;code&gt;snap set&lt;/code&gt; with key-value pairs, snapd calls the &lt;code&gt;configure&lt;/code&gt; hook. If the &lt;code&gt;configure&lt;/code&gt; hook exits successfully, snapd applies (saves) the configuration. If the &lt;code&gt;configure&lt;/code&gt; hook exits non-zero, snapd considers the configuration bad and doesn&amp;rsquo;t save it. So all we need to do in the &lt;code&gt;configure&lt;/code&gt; hook is ensure that the sleep time is valid, and if not, exit non-zero.&lt;/p&gt;
&lt;p&gt;An important point before we move on: if the &lt;code&gt;configure&lt;/code&gt; hook exits non-zero during an install or a refresh, that operation is rolled back (e.g. if a refresh, the snap is rolled back to the previously-working snap). (How does the hook know that it&amp;rsquo;s running during one of those times? It doesn&amp;rsquo;t, which is one of the reasons I think this design was a mistake and you should use the &lt;code&gt;install&lt;/code&gt; and &lt;code&gt;post-refresh&lt;/code&gt; hooks for that stuff, but I digress again.)&lt;/p&gt;
&lt;p&gt;Alright, now that we have that out of the way, let&amp;rsquo;s build and install this snap (remember, &lt;code&gt;--dangerous&lt;/code&gt; because this isn&amp;rsquo;t from the store):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft
Preparing to pull my-service 
Pulling my-service 
Preparing to build my-service 
Building my-service 
Staging my-service 
Priming my-service 
Snapping &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;my-snap-name&amp;#39;&lt;/span&gt; |
Snapped my-snap-name_0.1_amd64.snap
$ sudo snap install my-snap-name_0.1_amd64.snap --dangerous
my-snap-name 0.1 installed
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now check the journal:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ journalctl -fu snap.my-snap-name.hellod.service 
-- Logs begin at Mon 2017-09-11 07:51:07 PDT. --
Sep &lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt; 13:35:40 Pandora my-snap-name.hellod&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;18026&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello, World!
Sep &lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt; 13:35:45 Pandora my-snap-name.hellod&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;18026&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello, World!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As expected, we see a greeting every 5 seconds. We can verify that configuration setting using &lt;code&gt;snap get&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snap get my-snap-name sleep-time
&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There we see the value that we set in the &lt;code&gt;install&lt;/code&gt; hook. We can change it using &lt;code&gt;snap set&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snap set my-snap-name sleep-time&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
$ snap get my-snap-name sleep-time
&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can see that the new value applied. Let&amp;rsquo;s check the journal:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ journalctl -fu snap.my-snap-name.hellod.service 
-- Logs begin at Mon 2017-09-11 07:51:07 PDT. --
Sep &lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt; 13:40:17 Pandora my-snap-name.hellod&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;18026&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello, World!
Sep &lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt; 13:40:18 Pandora my-snap-name.hellod&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;18026&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello, World!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There we go, a greeting once a second. Now let&amp;rsquo;s see what happens if we try to set an invalid value:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snap set my-snap-name sleep-time&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;foo
error: cannot perform the following tasks:
- Run configure hook of &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-snap-name&amp;#34;&lt;/span&gt; snap &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;run hook &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;configure&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;foo&amp;#34;&lt;/span&gt; is not a valid
 sleep time&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
$ snap get my-snap-name sleep-time
&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, the invalid value was caught by our &lt;code&gt;configure&lt;/code&gt; script, and it was not applied.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I hope you can see how this might apply to your snap, and how giving your product a consistent interface for configuration might lead to a better overall user experience than requiring them to edit configuration files. It&amp;rsquo;s also easier for you to validate!&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Snap install-time setup: the install hook</title>
      <link>https://kyrofa.com/posts/snap-install-time-setup-the-install-hook/</link>
      <pubDate>Mon, 11 Sep 2017 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/snap-install-time-setup-the-install-hook/</guid>
      <description>When it comes to developing snaps, there&amp;rsquo;s a particular confusion out there that I see over and over again: build-time versus run-time. For example: &amp;ldquo;I&amp;rsquo;m building a snap, but I can&amp;rsquo;t seem to convince Snapcraft to place my config file in $SNAP_DATA.&amp;rdquo; In this post, I want to show you how to get the results you want.
First of all, we need to clear something up. Snapcraft can&amp;rsquo;t actually do this for you.</description>
      <content:encoded>&lt;p&gt;When it comes to developing snaps, there&amp;rsquo;s a particular confusion out there that I see over and over again: build-time versus run-time. For example: &amp;ldquo;I&amp;rsquo;m building a snap, but I can&amp;rsquo;t seem to convince Snapcraft to place my config file in &lt;code&gt;$SNAP_DATA&lt;/code&gt;.&amp;rdquo; In this post, I want to show you how to get the results you want.&lt;/p&gt;
&lt;p&gt;First of all, we need to clear something up. Snapcraft can&amp;rsquo;t actually do this for you. Why? Because Snapcraft is responsible only for building the &lt;code&gt;my-cool-thing_v1.0.snap&lt;/code&gt;. Once that snap is built, you can install it. Before the snap is installed/running (e.g. while you&amp;rsquo;re building the snap), &lt;code&gt;$SNAP&lt;/code&gt;, &lt;code&gt;$SNAP_DATA&lt;/code&gt;, etc. &lt;em&gt;do not exist&lt;/em&gt;. They are defined by snapd when the snap is installed.&lt;/p&gt;
&lt;p&gt;To put it another way, the snap created by Snapcraft is completely immutable and self-contained. The snap does not contain &lt;code&gt;$SNAP_DATA&lt;/code&gt;, and has no concept of &lt;code&gt;$SNAP_DATA&lt;/code&gt; until it is actually installed. Which means Snapcraft doesn&amp;rsquo;t have that concept either, and can&amp;rsquo;t help you.&lt;/p&gt;
&lt;p&gt;However, as of v2.27, snapd &lt;em&gt;does&lt;/em&gt; have the ability to help you, by way of the &lt;code&gt;install&lt;/code&gt; hook. Generically, hooks are the way snapd tells a snap that something interesting has happened, and gives it a chance to do something about it. At its most basic, a hook is an executable contained in the snap that snapd calls when it feels like it should. In the case of the &lt;code&gt;install&lt;/code&gt; hook, it&amp;rsquo;s letting the snap know that it&amp;rsquo;s currently being installed, and provides the opportunity for the snap to do something as part of that process, before services are fired up, and potentially bail out of the entire install process if required.&lt;/p&gt;
&lt;p&gt;To be clear: the &lt;code&gt;install&lt;/code&gt; hook runs only upon initial install, not a refresh or remove (there are other hooks for those situations).&lt;/p&gt;
&lt;p&gt;Let me explain with a tutorial.&lt;/p&gt;
&lt;h2 id=&#34;tutorial&#34;&gt;Tutorial&lt;/h2&gt;
&lt;h3 id=&#34;the-problem&#34;&gt;The problem&lt;/h3&gt;
&lt;p&gt;First of all, let&amp;rsquo;s get started by creating a brand new snap that suffers from a pretty typical problem: a service that requires a config file, but that config file needs to be in a writable place (e.g. &lt;code&gt;$SNAP_DATA&lt;/code&gt;). Once we have that, we&amp;rsquo;ll discuss how the &lt;code&gt;install&lt;/code&gt; hook solves that problem.&lt;/p&gt;
&lt;p&gt;So, get into the directory that will contain our snap, and run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft init
Created snap/snapcraft.yaml.
Edit the file to your liking or run &lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;snapcraft&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt; to get started
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, let&amp;rsquo;s create our service. We&amp;rsquo;ll keep it simple: this service simply says &amp;ldquo;Hello, World!&amp;rdquo; over and over again, at a rate determined by a config file. First, create the service executable itself, and make sure it&amp;rsquo;s executable:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ mkdir -p src/bin
$ touch src/bin/hellod
$ chmod a+x src/bin/hellod
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now make that file look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/sh -e
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
config_file&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$SNAP_DATA&lt;span style=&#34;color:#e6db74&#34;&gt;/hello.conf&amp;#34;&lt;/span&gt;

&lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; true; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
   &lt;span style=&#34;color:#75715e&#34;&gt;# First, determine our rate by determining how long we should sleep&lt;/span&gt;
   sleep_time&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;awk &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/^sleep_time/{print $2}&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$config_file&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;

   &lt;span style=&#34;color:#75715e&#34;&gt;# Now be nice and greet&lt;/span&gt;
   echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello, World!&amp;#34;&lt;/span&gt;

   &lt;span style=&#34;color:#75715e&#34;&gt;# Now sleep for the time specified in the config file&lt;/span&gt;
   sleep &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$sleep_time&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Update your &lt;code&gt;snap/snapcraft.yaml&lt;/code&gt; to include this service by adding both a &lt;code&gt;part&lt;/code&gt; to install it as well as an &lt;code&gt;app&lt;/code&gt; to expose it as a service. While we&amp;rsquo;re at it, let&amp;rsquo;s change the confinement type to &lt;code&gt;strict&lt;/code&gt; instead of &lt;code&gt;devmode&lt;/code&gt;. Make it look something like this (most of it is still the template):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;my-snap-name&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# you probably want to &amp;#39;snapcraft register &amp;lt;name&amp;gt;&amp;#39;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0.1&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# just for humans, typically &amp;#39;1.2+git&amp;#39; or &amp;#39;1.3.2&amp;#39;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;summary&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Single-line elevator pitch for your amazing snap&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# 79 char long summary&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;description&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; This is my-snap&amp;#39;s description. You have a paragraph or two to tell the
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; most important story about your snap. Keep it under 100 words though,
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; we live in tweetspace and your description wants to look good in the snap
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; store.&lt;/span&gt; 

&lt;span style=&#34;color:#f92672&#34;&gt;grade&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;devel&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# must be &amp;#39;stable&amp;#39; to release into candidate/stable channels&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;confinement&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;strict&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;parts&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;my-service&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;dump&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;source&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;src/&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;apps&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;hellod&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;command&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;hellod&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;daemon&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;simple&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s go ahead and build this snap, and then install it (&lt;code&gt;--dangerous&lt;/code&gt; because it&amp;rsquo;s not from the store):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft
Preparing to pull my-service 
Pulling my-service 
Preparing to build my-service 
Building my-service 
Staging my-service 
Priming my-service 
Snapping &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;my-snap-name&amp;#39;&lt;/span&gt; |
Snapped my-snap-name_0.1_amd64.snap
$ sudo snap install my-snap-name_0.1_amd64.snap --dangerous
my-snap-name 0.1 installed
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now as you probably know, as soon as you install the snap, its services are fired up. Let&amp;rsquo;s take a look at the output from our service:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ journalctl -u snap.my-snap-name.hellod.service 
-- Logs begin at Mon 2017-09-11 07:51:07 PDT, end at Mon 2017-09-11 10:26:03 PDT. --
Sep &lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt; 10:23:18 Pandora systemd&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Started Service &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; snap application my-snap-name.hellod.
Sep &lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt; 10:23:19 Pandora my-snap-name.hellod&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;8527&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: awk: fatal: cannot open file &lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;/var/snap/my-snap-name/x1/hello.conf&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; reading &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;No such file or directory&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
Sep &lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt; 10:23:19 Pandora systemd&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: snap.my-snap-name.hellod.service: Main process exited, code&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;exited, status&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;2/INVALIDARGUMENT
Sep &lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt; 10:23:19 Pandora systemd&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: snap.my-snap-name.hellod.service: Unit entered failed state.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ah, well darn, our service is failing because it can&amp;rsquo;t find the config file we didn&amp;rsquo;t put there&amp;ndash; how unexpected!&lt;/p&gt;
&lt;p&gt;Sarcasm aside, while this example is contrived, this is a very common problem. Oftentimes config files need to be writable by the user, which means they simply cannot be held within the snap itself, which is by definition immutable. However, one is left with the problem of needing to get a config file in a writable area before the service that needs it is started, exactly like this example. Before snapd v2.27, the only solution was to wrap one&amp;rsquo;s service in a shell script that checked for the config file and created it if it wasn&amp;rsquo;t there. This was tedius and annoying, but no more! Let&amp;rsquo;s solve this the Right Way™ by creating an &lt;code&gt;install&lt;/code&gt; hook. First though, let&amp;rsquo;s remove the snap we already have installed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap remove my-snap-name
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;the-solution&#34;&gt;The solution&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s start by creating a new directory to hold our hooks:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ mkdir snap/hooks
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now let&amp;rsquo;s create the install hook, and make sure it&amp;rsquo;s executable:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ touch snap/hooks/install
$ chmod a+x snap/hooks/install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we&amp;rsquo;ll make that file look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/sh -e
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# Create a default config file&lt;/span&gt;
echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sleep_time 5&amp;#34;&lt;/span&gt; &amp;gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$SNAP_DATA&lt;span style=&#34;color:#e6db74&#34;&gt;/hello.conf&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Believe it or not, that&amp;rsquo;s it. Snapcraft knows this is a hook because you put it in the &lt;code&gt;snap/hooks/&lt;/code&gt; dir and it&amp;rsquo;s executable, and it&amp;rsquo;ll place it in the right spot in the snap. Snapd knows it&amp;rsquo;s the &lt;code&gt;install&lt;/code&gt; hook because the file name is &amp;ldquo;install&amp;rdquo;. Note that the code for this snap is &lt;a href=&#34;https://github.com/kyrofa/install-hook-tutorial&#34;&gt;available on GitHub&lt;/a&gt; for reference.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; Hooks run confined, just like apps. This particular hook requires no special permission, so nothing extra was required for it to work. However, let&amp;rsquo;s say our hook required access to the network. In that case, it would need to use the &lt;code&gt;network&lt;/code&gt; plug the same way as an app would need to. The way to specify this requirement is to add a section to your YAML that looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;hooks&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;install&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;plugs&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;network]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Again, that&amp;rsquo;s not required here, but keep it in mind.&lt;/p&gt;
&lt;p&gt;Alright back to it: let&amp;rsquo;s build the snap from scratch again, and install it once more:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft clean
Cleaning up priming area
Cleaning up staging area
Cleaning up parts directory
$ snapcraft
Preparing to pull my-service 
Pulling my-service 
Preparing to build my-service 
Building my-service 
Staging my-service 
Priming my-service 
Snapping &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;my-snap-name&amp;#39;&lt;/span&gt; |
Snapped my-snap-name_0.1_amd64.snap
$ sudo snap install my-snap-name_0.1_amd64.snap --dangerous
my-snap-name 0.1 installed
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now take a look at the service journal, and you&amp;rsquo;ll see it working:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ journalctl -fu snap.my-snap-name.hellod.service 
-- Logs begin at Mon 2017-09-11 07:51:07 PDT. --
Sep &lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt; 10:59:54 Pandora my-snap-name.hellod&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;11625&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello, World!
Sep &lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt; 10:59:59 Pandora my-snap-name.hellod&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;11625&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello, World!
Sep &lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt; 11:00:04 Pandora my-snap-name.hellod&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;11625&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello, World!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;How is this working? Well, now that you provided an &lt;code&gt;install&lt;/code&gt; hook, snapd ran it as part of the installation. You can see specifically when by taking a look at the change representing the snap installation. First, we need to determine the change ID:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snap changes
&amp;lt;snip&amp;gt;
&lt;span style=&#34;color:#ae81ff&#34;&gt;1138&lt;/span&gt; Done &amp;lt;snip&amp;gt; Install &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-snap-name&amp;#34;&lt;/span&gt; snap from file &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-snap-name_0.1_amd64.snap&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now take a look at that particular change:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snap change &lt;span style=&#34;color:#ae81ff&#34;&gt;1138&lt;/span&gt;
Status &amp;lt;snip&amp;gt; Summary
Done           Prepare snap &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/tmp/snapd-sideload-pkg-840816098&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;unset&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
Done           Mount snap &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-snap-name&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;unset&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
Done           Copy snap &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-snap-name&amp;#34;&lt;/span&gt; data
Done           Setup snap &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-snap-name&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;unset&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; security profiles
Done           Make snap &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-snap-name&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;unset&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; available to the system
Done           Setup snap &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-snap-name&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;unset&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; security profiles &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;phase 2&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
Done           Set automatic aliases &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; snap &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-snap-name&amp;#34;&lt;/span&gt;
Done           Setup snap &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-snap-name&amp;#34;&lt;/span&gt; aliases
Done           Run install hook of &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-snap-name&amp;#34;&lt;/span&gt; snap &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; present
Done           Start snap &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-snap-name&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;unset&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; services
Done           Run configure hook of &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-snap-name&amp;#34;&lt;/span&gt; snap &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; present
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note in particular where the &amp;lsquo;Run install hook of &amp;ldquo;my-snap-name&amp;rdquo; snap if present&amp;rsquo; is happening: right &lt;em&gt;before&lt;/em&gt; services are started. Which gives it a chance to set things up so that services have what they require before they try to run. If the &lt;code&gt;install&lt;/code&gt; hook runs into a problem, it can exit non-zero and cause the entire installation to abort.&lt;/p&gt;
&lt;p&gt;After installation, one can update that config file to shorten or lengthen the sleep time as required. While that may not be particularly interesting for this snap, imagine if this was an Apache configuration file, or MySQL. Depending on the snap, it may be quite useful to expose the configuration this way, and of course can be used for other things as well (e.g. plugins, assets, generate an initial database, etc.) In a &lt;a href=&#34;https://kyrofa.com/posts/snap-configuration-the-configure-hook&#34;&gt;follow-up post&lt;/a&gt;, we&amp;rsquo;ll discuss another way to manage configurations, by way of the &lt;code&gt;configure&lt;/code&gt; hook.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Integrating your Rails project with GitLab review apps</title>
      <link>https://kyrofa.com/posts/integrating-your-rails-project-with-gitlab-review-apps/</link>
      <pubDate>Mon, 24 Jul 2017 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/integrating-your-rails-project-with-gitlab-review-apps/</guid>
      <description>My first foray into software development when I was a kid was as a web developer. I don&amp;rsquo;t admit this to many people, but the first language I ever learned halfway decently was PHP. I led a confused childhood. After that, though, I was introduced to Ruby on Rails, and I&amp;rsquo;ve been using it ever since.
I don&amp;rsquo;t do web development professionally anymore, but I have a number of web-based side projects, making websites for friends or family.</description>
      <content:encoded>&lt;p&gt;My first foray into software development when I was a kid was as a web developer. I don&amp;rsquo;t admit this to many people, but the first language I ever learned halfway decently was PHP. I led a confused childhood. After that, though, I was introduced to Ruby on Rails, and I&amp;rsquo;ve been using it ever since.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t do web development professionally anymore, but I have a number of web-based side projects, making websites for friends or family. Most of the time, the people I work with are the brains of the operation, and I&amp;rsquo;m the brawn: they decide what they want, and I make it for them. Also, most of the time the people I&amp;rsquo;m working with are non-technical. So as I make progress on their project and want to obtain feedback, I&amp;rsquo;m rarely looking for feedback on my code (that&amp;rsquo;s always perfect anyway, of course). I want them to play around with the site itself.&lt;/p&gt;
&lt;p&gt;There are some obvious ways to do that. We could meet in person and they could play with it on my laptop, or they could VPN in and access my local server remotely, or I could deploy to a staging server, and so on. These are fine solutions, but they&amp;rsquo;re synchronous and don&amp;rsquo;t scale well. Perhaps more importantly, they get in my way: they can only review essentially one thing at a time.&lt;/p&gt;
&lt;p&gt;These projects are all using Rails 5, and are hosted on a private GitLab instance. While doing some research into how I might be able to move a bit faster, I read about GitLab&amp;rsquo;s &lt;a href=&#34;https://about.gitlab.com/features/review-apps/&#34;&gt;Review Apps&lt;/a&gt;, and realized I&amp;rsquo;d hit a gold mine. This post documents how I got it working for my Rails projects in the hope that it will be useful to you.
Review Apps&lt;/p&gt;
&lt;p&gt;I was already using Gitlab CI to test my rails project, as well as deploy it to staging and production. Deploying requires the use of &lt;a href=&#34;https://docs.gitlab.com/ee/ci/environments.html&#34;&gt;environments&lt;/a&gt;, and once you have those figured out review apps build naturally on top of them. Deploying is pretty cool: once the deploy finishes successfully, GitLab shows the URL you specified in the environment as a way to view the deployment, which makes things pretty seamless. Well, review apps are just like that, but the environment is actually &lt;a href=&#34;https://docs.gitlab.com/ee/ci/environments.html#dynamic-environments&#34;&gt;&lt;em&gt;dynamic&lt;/em&gt;&lt;/a&gt;. The environment &lt;code&gt;name&lt;/code&gt; is dynamic, and the environment &lt;code&gt;url&lt;/code&gt; is dynamic. These are typically based on the branch name. It means you get a separate deployment per branch, which means if you structure your project correctly, you can get nicely isolated deployments for every merge request you make, even at the same time.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Review apps aren&amp;rsquo;t magical. You need the infrastructure to support them.&lt;/p&gt;
&lt;h3 id=&#34;ability-to-handle-unique-branch-based-urls&#34;&gt;Ability to handle unique, branch-based URLs&lt;/h3&gt;
&lt;p&gt;Review apps will just assume that a URL it generates by combining the branch name with the base domain you specify actually points to the app in the branch. We&amp;rsquo;ll get into the technical details of this in a bit, but in my case, my URL looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;url&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;http://$CI_COMMIT_REF_SLUG.example.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So if I push up a branch named &lt;code&gt;support_review_apps&lt;/code&gt; that will turn into &lt;code&gt;http://support-review-apps.example.com&lt;/code&gt;. Review apps does nothing to ensure that exists, it just assumes it does. In my case, that means I needed to have a wildcard DNS entry for &lt;code&gt;*.example.com&lt;/code&gt;. Since my registrar is 1&amp;amp;1 (who doesn&amp;rsquo;t support wildcard DNS entries), I started using &lt;a href=&#34;https://www.namecheap.com/&#34;&gt;Namecheap&lt;/a&gt; for my DNS. It&amp;rsquo;s pretty awesome. If you&amp;rsquo;re unfamiliar, you essentially create a &lt;code&gt;*.example.com&lt;/code&gt; subdomain pointing to a given IP. Then &lt;code&gt;foo.example.com&lt;/code&gt;, which doesn&amp;rsquo;t have an entry of its own, will resolve to that same IP. So will &lt;code&gt;bar.example.com&lt;/code&gt;, you get the idea. Why is this useful? Because then we can use it in our web server to make sure that traffic ends up in the right place. We&amp;rsquo;ll get to that in a minute.&lt;/p&gt;
&lt;h3 id=&#34;somewhere-you-can-dynamically-deploy&#34;&gt;Somewhere you can dynamically deploy&lt;/h3&gt;
&lt;p&gt;This might look like docker to you. Or a kubernetes cluster. For me, it&amp;rsquo;s a single LXD container configured for my project (the right version of Ruby, etc.) and using &lt;code&gt;bundler&lt;/code&gt; in &lt;code&gt;--deployment&lt;/code&gt; mode to put all the gems in a single tree instead of installing them on the system. Then I can install different branches of the same project in different directories using &lt;a href=&#34;https://github.com/capistrano/capistrano&#34;&gt;capistrano&lt;/a&gt;, which we&amp;rsquo;ll cover in a minute.&lt;/p&gt;
&lt;h2 id=&#34;get-on-with-it&#34;&gt;Get on with it&lt;/h2&gt;
&lt;h3 id=&#34;configuring-the-project&#34;&gt;Configuring the project&lt;/h3&gt;
&lt;p&gt;So we need to deploy the same project multiple times to the same infrastructure. This is a perfect use-case for docker, but that doesn&amp;rsquo;t run well within LXD (&lt;strong&gt;update:&lt;/strong&gt; it does nowadays if you enable nesting), so I was limited to using a single container. Since I wanted to keep each instance isolated, and also because they didn&amp;rsquo;t need to be particularly performant, I didn&amp;rsquo;t want to have to deal with a shared, server-based database, with different credentials depending on instance, etc. In other words, I decided that the Rails environment I wanted to use was not going to be production, where I use PostgreSQL. So I created a new environment called &amp;ldquo;review&amp;rdquo; in &lt;code&gt;config/environments/&lt;/code&gt;. It&amp;rsquo;s really just a copy of the &amp;ldquo;production&amp;rdquo; environment, but it allows us to specify a database for that environment in &lt;code&gt;config/database.yml&lt;/code&gt; that looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;review&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;: &lt;span style=&#34;color:#75715e&#34;&gt;*default&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;database&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;db/review.sqlite3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now our database is specific to that instance. We&amp;rsquo;ll also need a secret key base for that environment in &lt;code&gt;config/secrets.yml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;review&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;secret_key_base&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;9c9cdd6&amp;lt;snip&amp;gt;dd5667b&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Of course for production this should be in the environment rather than being committed, but I&amp;rsquo;m okay with this being hard-coded for review.&lt;/p&gt;
&lt;h3 id=&#34;configuring-the-web-server&#34;&gt;Configuring the web server&lt;/h3&gt;
&lt;p&gt;So we have a dynamic DNS pointing to an LXD container nearly setup for dynamic deployment. We still need to answer the question: &amp;ldquo;How do we ensure that traffic to a branch-based URL ends up on that branch&amp;rsquo;s deployed site?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Since all deployments for review apps on this project were going to the same container, I configured nginx to be smart about it, and grab the name from the URL in order to figure out where the traffic should go. That configuration looked like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;server {
   listen 80;
   server_name ~^(www\.)?(?&amp;lt;sname&amp;gt;.+?).example.com$;
   root /var/www/$sname/current/public;

   location / {
       proxy_pass http://unix:/var/www/$sname/shared/tmp/sockets/puma.sock;
       proxy_set_header Host $host;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
   }

   location ~* ^/assets/ {
       expires 1y;
       add_header Cache-Control public;
       add_header Last-Modified &amp;quot;&amp;quot;;
       add_header ETag &amp;quot;&amp;quot;;
       break;
   }

   access_log /var/log/nginx/$sname-access.log;
   error_log /var/log/nginx/reviews-error.log debug;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Since &lt;code&gt;*.example.com&lt;/code&gt; was already hitting nginx, this made it so visiting &lt;code&gt;foo.example.com&lt;/code&gt; tried to proxy to a puma socket in &lt;code&gt;/var/www/foo/shared/tmp/sockets/puma.sock&lt;/code&gt;. Of course, if that didn&amp;rsquo;t exist nginx told you so. Now we just needed a deployment method that actually deployed sites in sub-directories of &lt;code&gt;/var/www/&lt;/code&gt; based on branch name.&lt;/p&gt;
&lt;h3 id=&#34;configuring-capistrano-to-deploy-the-way-we-need&#34;&gt;Configuring capistrano to deploy the way we need&lt;/h3&gt;
&lt;p&gt;All my apps are using puma. So to deploy with capistrano, I add the following to the development group of my Gemfile:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;gem &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;capistrano&amp;#39;&lt;/span&gt;
gem &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;capistrano-rails&amp;#39;&lt;/span&gt;
gem &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;capistrano3-puma&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I don&amp;rsquo;t intend to make this post a capistrano tutorial, there are already several out there. However, assuming you already have it set up, there are three things we need to change from the typical capistrano behavior.&lt;/p&gt;
&lt;p&gt;First of all, since we want to use the new &amp;ldquo;review&amp;rdquo; environment, make sure you create an environment-specific deploy script in &lt;code&gt;config/deploy/review.rb&lt;/code&gt; (just copy the &amp;ldquo;production&amp;rdquo; one and modify as necessary). Since I&amp;rsquo;m using an &lt;code&gt;sqlite&lt;/code&gt; database, I want to make sure it&amp;rsquo;s shared across revisions of the deployment, so in that file I placed code to do exactly that:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;append &lt;span style=&#34;color:#e6db74&#34;&gt;:linked_files&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;db/review.sqlite3&amp;#34;&lt;/span&gt;

namespace &lt;span style=&#34;color:#e6db74&#34;&gt;:deploy&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
   namespace &lt;span style=&#34;color:#e6db74&#34;&gt;:check&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
       &lt;span style=&#34;color:#75715e&#34;&gt;# The database needs to exist before it&amp;#39;s symlinked in or the symlink&lt;/span&gt;
       &lt;span style=&#34;color:#75715e&#34;&gt;# will fail.&lt;/span&gt;
       before &lt;span style=&#34;color:#e6db74&#34;&gt;:linked_files&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;:create_db&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
           on primary &lt;span style=&#34;color:#e6db74&#34;&gt;:db&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
               execute &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;touch &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;#{&lt;/span&gt;shared_path&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/db/review.sqlite3&amp;#34;&lt;/span&gt;
           &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
       &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
   &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Second, by default, capistrano deploys to &lt;code&gt;/var/www/&amp;lt;application&amp;gt;&lt;/code&gt;. &lt;code&gt;capistrano3-puma&lt;/code&gt; also uses the application name to determine where to place puma&amp;rsquo;s socket. So if we can support changing the application name on the fly, we can support the branch-specific nesting that our nginx configuration will be expecting. We could use rake parameters for this, but I liked environment variables better, so in &lt;code&gt;config/deploy.rb&lt;/code&gt; I set the application like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;set &lt;span style=&#34;color:#e6db74&#34;&gt;:application&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;ENV&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fetch(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;application&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;example.com&amp;#39;&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So it defaults to the production application name, but now I can call &lt;code&gt;cap review deploy application=foo&lt;/code&gt; and it&amp;rsquo;ll deploy to &lt;code&gt;/var/www/foo/&lt;/code&gt;, which is exactly what I want.&lt;/p&gt;
&lt;p&gt;Third, by default capistrano deploys the &lt;code&gt;master&lt;/code&gt; branch. With review apps we want to deploy a specific branch, so that&amp;rsquo;s another line in &lt;code&gt;config/deploy.rb&lt;/code&gt; like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;set &lt;span style=&#34;color:#e6db74&#34;&gt;:branch&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;ENV&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fetch(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;branch&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;master&amp;#39;&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Still defaulting to &lt;code&gt;master&lt;/code&gt;, but now we can call &lt;code&gt;cap review deploy branch=my-branch-name&lt;/code&gt; and it&amp;rsquo;ll deploy the &lt;code&gt;my-branch-name&lt;/code&gt; branch instead of &lt;code&gt;master&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, what we&amp;rsquo;ve discussed so far has a &amp;ldquo;leak&amp;rdquo; issue. Every branch that&amp;rsquo;s pushed will be deployed onto this same container. That deployment stays there, even after the branch has been merged and deleted! Eventually we&amp;rsquo;ll run out of hard drive space. Thankfully, review apps support stopping once the branch is deleted, we just need to support such a thing. I added a simple &lt;code&gt;undeploy&lt;/code&gt; task to &lt;code&gt;config/deploy.rb&lt;/code&gt; that looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;namespace &lt;span style=&#34;color:#e6db74&#34;&gt;:deploy&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
   task &lt;span style=&#34;color:#e6db74&#34;&gt;:undeploy&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
       on roles(&lt;span style=&#34;color:#e6db74&#34;&gt;:app&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
           invoke &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;puma:stop&amp;#39;&lt;/span&gt;
           execute &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rm -rf &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;#{&lt;/span&gt;fetch(&lt;span style=&#34;color:#e6db74&#34;&gt;:deploy_to&lt;/span&gt;)&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
       &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
   &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So then stopping and removing a deployment is as simple as calling &lt;code&gt;cap review deploy:undeploy&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;putting-all-of-this-together-and-using-review-apps&#34;&gt;Putting all of this together and using review apps&lt;/h3&gt;
&lt;p&gt;We now have a Rails project with a &amp;ldquo;review&amp;rdquo; environment, a capistrano configuration for deploying that environment, and the basic infrastructure necessary to host the deployments. Now we just need to flip the switch to enable GitLab Review Apps.&lt;/p&gt;
&lt;p&gt;That of course goes into the &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; file. You probably already had a &lt;code&gt;deploy&lt;/code&gt; stage, you just need to add jobs for deploying and stopping the review apps, which looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;deploy_review&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;stage&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;deploy&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;tags&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;ruby-2.4.1]&lt;/span&gt;

 &lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;:
   - &lt;span style=&#34;color:#ae81ff&#34;&gt;eval &amp;#34;$(ssh-agent -s)&amp;#34;&lt;/span&gt;
   - &lt;span style=&#34;color:#ae81ff&#34;&gt;ssh-add &amp;lt;(echo -e &amp;#34;$SSH_PRIVATE_KEY&amp;#34;)&lt;/span&gt;
   - &lt;span style=&#34;color:#ae81ff&#34;&gt;bundle exec cap review deploy branch=$CI_COMMIT_REF_NAME application=$CI_COMMIT_REF_SLUG&lt;/span&gt;

 &lt;span style=&#34;color:#f92672&#34;&gt;environment&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;review/$CI_COMMIT_REF_NAME&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;url&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;http://$CI_COMMIT_REF_SLUG.example.com&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;on_stop&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;stop_review&lt;/span&gt;

 &lt;span style=&#34;color:#f92672&#34;&gt;only&lt;/span&gt;:
   - &lt;span style=&#34;color:#ae81ff&#34;&gt;branches&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;except&lt;/span&gt;:
   - &lt;span style=&#34;color:#ae81ff&#34;&gt;master&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;stop_review&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;stage&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;deploy&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;tags&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;ruby-2.4.1]&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;variables&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;GIT_STRATEGY&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;none&lt;/span&gt;

 &lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;:
   - &lt;span style=&#34;color:#ae81ff&#34;&gt;eval &amp;#34;$(ssh-agent -s)&amp;#34;&lt;/span&gt;
   - &lt;span style=&#34;color:#ae81ff&#34;&gt;ssh-add &amp;lt;(echo -e &amp;#34;$SSH_PRIVATE_KEY&amp;#34;)&lt;/span&gt;
   - &lt;span style=&#34;color:#ae81ff&#34;&gt;bundle exec cap review deploy:undeploy application=$CI_COMMIT_REF_SLUG&lt;/span&gt;

 &lt;span style=&#34;color:#f92672&#34;&gt;environment&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;review/$CI_COMMIT_REF_NAME&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;action&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;stop&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;when&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;manual&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s walk through the deployment job first, piece by piece.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;deploy_review&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;stage&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;deploy&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;tags&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;ruby-2.4.1]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This defines the job, putting it into the &lt;code&gt;deploy&lt;/code&gt; stage (which in my pipeline comes after &lt;code&gt;test&lt;/code&gt;). It needs a runner with Ruby v2.4.1 on it, so I tag it as such.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt; &lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;:
   - &lt;span style=&#34;color:#ae81ff&#34;&gt;eval &amp;#34;$(ssh-agent -s)&amp;#34;&lt;/span&gt;
   - &lt;span style=&#34;color:#ae81ff&#34;&gt;ssh-add &amp;lt;(echo -e &amp;#34;$SSH_PRIVATE_KEY&amp;#34;)&lt;/span&gt;
   - &lt;span style=&#34;color:#ae81ff&#34;&gt;bundle exec cap review deploy branch=$CI_COMMIT_REF_NAME application=$CI_COMMIT_REF_SLUG&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is where the rubber meets the road. Capistrano works over SSH, so I have a private key saved as a variable in GitLab that I load into ssh-agent and then deploy using the &lt;code&gt;$branch&lt;/code&gt; and &lt;code&gt;$application&lt;/code&gt; environment variables we discussed earlier. I use &lt;code&gt;$CI_COMMIT_REF_SLUG&lt;/code&gt; for the application name to ensure that it&amp;rsquo;s safe for use in a URL.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt; &lt;span style=&#34;color:#f92672&#34;&gt;environment&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;review/$CI_COMMIT_REF_NAME&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;url&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;http://$CI_COMMIT_REF_SLUG.example.com&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;on_stop&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;stop_review&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s the dynamic environment. Note how the &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;url&lt;/code&gt; both use the branch name (although the &lt;code&gt;url&lt;/code&gt; uses the slug to ensure it&amp;rsquo;s safe for use in a URL). This is the URL that will be shown on a merge request to view that deployment. Finally, we specify the job that needs to run in order to stop this deployment, &lt;code&gt;stop_review&lt;/code&gt;. GitLab will run this job automatically once the branch is deleted. Let&amp;rsquo;s take a look at that job now, piece by piece.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;stop_review&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;stage&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;deploy&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;tags&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;ruby-2.4.1]&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;variables&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;GIT_STRATEGY&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;none&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here again we name the job, and specify that it requires a runner with Ruby v2.4.1. We also specify &lt;code&gt;GIT_STRATEGY: none&lt;/code&gt; so the runner doesn&amp;rsquo;t try to check out code that was deleted. This is a little odd to me since I&amp;rsquo;m still able to use capistrano here, so I wonder if I&amp;rsquo;m taking advantage of the fact that this ends up running on the same runner and the code is already there. I need to investigate that further, but this works for now.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt; &lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;:
   - &lt;span style=&#34;color:#ae81ff&#34;&gt;eval &amp;#34;$(ssh-agent -s)&amp;#34;&lt;/span&gt;
   - &lt;span style=&#34;color:#ae81ff&#34;&gt;ssh-add &amp;lt;(echo -e &amp;#34;$SSH_PRIVATE_KEY&amp;#34;)&lt;/span&gt;
   - &lt;span style=&#34;color:#ae81ff&#34;&gt;bundle exec cap review deploy:undeploy application=$CI_COMMIT_REF_SLUG&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As before, we load the SSH key into &lt;code&gt;ssh-agent&lt;/code&gt;, and then execute our &lt;code&gt;undeploy&lt;/code&gt; job with the same &lt;code&gt;application&lt;/code&gt; variable to make sure we blow away the right one.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt; &lt;span style=&#34;color:#f92672&#34;&gt;environment&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;review/$CI_COMMIT_REF_NAME&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;action&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;stop&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;when&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;manual&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that this environment name corresponds to the &lt;code&gt;deploy_review&lt;/code&gt; job as well, and the action is &lt;code&gt;stop&lt;/code&gt;, which tells GitLab this is how it stops that environment. We specify that this is a manual action so it doesn&amp;rsquo;t happen when we push, but it&amp;rsquo;s still automatic since we specified this job name in the &lt;code&gt;on_stop&lt;/code&gt; of the initial deployment.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;It took me a while to distill the documentation down into those steps, but once you&amp;rsquo;ve gone through the process you realize that it makes sense and it&amp;rsquo;s easy to do again. This has &lt;em&gt;drastically&lt;/em&gt; sped up my development time as reviews can now happen asynchronously and simultaneously. I really can&amp;rsquo;t recommend it enough, and I hope this post helps you accomplish the same thing!&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Ubuntu Core: Making a factory image with private snaps</title>
      <link>https://kyrofa.com/posts/ubuntu-core-making-a-factory-image-with-private-snaps/</link>
      <pubDate>Thu, 06 Jul 2017 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/ubuntu-core-making-a-factory-image-with-private-snaps/</guid>
      <description>This is a follow-up to the ROS prototype to production on Ubuntu Core series to answer a question I received: &amp;ldquo;What if I want to make an image for the factory, but don&amp;rsquo;t want to make my snaps public?&amp;rdquo; This question is of course not robotics-specific, and neither is its answer. In this post we&amp;rsquo;ll cover two ways to do this.
Before we start, you&amp;rsquo;ll need a little bit of an Ubuntu Core imaging background.</description>
      <content:encoded>&lt;p&gt;This is a follow-up to the &lt;a href=&#34;https://kyrofa.com/posts/from-ros-prototype-to-production-on-ubuntu-core-1-5&#34;&gt;ROS prototype to production on Ubuntu Core&lt;/a&gt; series to answer a question I received: &amp;ldquo;What if I want to make an image for the factory, but don&amp;rsquo;t want to make my snaps public?&amp;rdquo; This question is of course not robotics-specific, and neither is its answer. In this post we&amp;rsquo;ll cover two ways to do this.&lt;/p&gt;
&lt;p&gt;Before we start, you&amp;rsquo;ll need a little bit of an Ubuntu Core imaging background. If you followed the ROS prototype to production series (&lt;a href=&#34;https://kyrofa.com/posts/ros-production-create-an-ubuntu-core-image-with-our-snap-preinstalled-5-5&#34;&gt;part 5&lt;/a&gt; specifically) you already have the required background, but if you didn&amp;rsquo;t, check out the &lt;a href=&#34;https://ubuntu.com/core/docs/image-building&#34;&gt;docs for creating your own Ubuntu Core image&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Assuming you&amp;rsquo;re up-to-speed and know what I&amp;rsquo;m talking about when I say &amp;ldquo;model definition&amp;rdquo; or &amp;ldquo;model assertion,&amp;rdquo; let&amp;rsquo;s get started on a few different methods for creating an Ubuntu Core image with private snaps.&lt;/p&gt;
&lt;h2 id=&#34;method-1-dont-put-your-snap-in-the-store-at-all&#34;&gt;Method 1: Don&amp;rsquo;t put your snap in the store at all&lt;/h2&gt;
&lt;p&gt;It really doesn&amp;rsquo;t get simpler. Take a look at this example model definition, &lt;code&gt;amd64-model.json&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;{
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;model&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;series&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;16&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;model&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;custom-amd64&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;architecture&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;amd64&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;gadget&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pc&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;kernel&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pc-kernel&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;authority-id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;4tSgWHfAL1vm9l8mSiutBDKnnSQBv0c8&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;brand-id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;4tSgWHfAL1vm9l8mSiutBDKnnSQBv0c8&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;timestamp&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2017-06-23T21:03:24+00:00&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;required-snaps&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kyrofa-test-snap&amp;#34;&lt;/span&gt;]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s go ahead and turn that into a model assertion:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cat amd64-model.json | snap sign -k my-key-name &amp;gt; amd64.model
You need a passphrase to unlock the secret key &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt;
user: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-key-name&amp;#34;&lt;/span&gt;
4096-bit RSA key, ID 0B79B865, created 2016-01-01
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you have your model assertion: &lt;code&gt;amd64.model&lt;/code&gt;. If you hand that to &lt;code&gt;ubuntu-image&lt;/code&gt; right now you&amp;rsquo;ll run into a problem:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo ubuntu-image -c stable amd64.model 
Fetching core
Fetching pc-kernel
Fetching pc
Fetching kyrofa-test-snap
error: cannot find snap &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kyrofa-test-snap&amp;#34;&lt;/span&gt;: snap not found
COMMAND FAILED: snap prepare-image --channel&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;stable amd64.model /tmp/tmp6p453gk9/unpack
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The snap with the name &lt;code&gt;kyrofa-test-snap&lt;/code&gt; isn&amp;rsquo;t actually in the store. But that&amp;rsquo;s important to note: the model definition (and thus assertion) only contains a list of snap &lt;em&gt;names&lt;/em&gt;. If you have a snap locally with that name, even if it&amp;rsquo;s not in the store, you can tell &lt;code&gt;ubuntu-image&lt;/code&gt; to use it to satisfy that name in the assertion with the &lt;code&gt;--extra-snaps&lt;/code&gt; option:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo ubuntu-image -c stable &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;                   --extra-snaps /path/to/kyrofa-test-snap_0.1_amd64.snap &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;                   amd64.model
Fetching core
Fetching pc-kernel
Fetching pc
Copying &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/path/to/kyrofa-test-snap_0.1_amd64.snap&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;kyrofa-test-snap&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
kyrofa-test-snap already prepared, skipping
WARNING: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kyrofa-test-snap&amp;#34;&lt;/span&gt; were installed from local snaps disconnected from
a store and cannot be refreshed subsequently!
Partition size/offset need to be a multiple of sector size &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;512&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;.
The size/offset will be rounded up to the nearest sector.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There. You now have an Ubuntu Core image (named &lt;code&gt;pc.img&lt;/code&gt;) with your snap preinstalled, without the snap ever needing to be in the store. This works, but it has a big disadvantage which &lt;code&gt;ubuntu-image&lt;/code&gt; points out with a warning: preinstalling a snap that isn&amp;rsquo;t connected to the store means you have no way to update it once devices are flashed with this image. Your only update mechanism would be to ship new images to be flashed.&lt;/p&gt;
&lt;h2 id=&#34;method-2-use-a-brand-store&#34;&gt;Method 2: Use a brand store&lt;/h2&gt;
&lt;p&gt;When you create a store account and visit &lt;a href=&#34;http://dashboard.snapcraft.io/&#34;&gt;dashboard.snapcraft.io&lt;/a&gt;, you&amp;rsquo;re viewing your snaps in the standard Ubuntu store. If you install snapd fresh on your system, this is the store it uses by default. While you can release snaps privately on the Ubuntu store, you &lt;a href=&#34;https://forum.snapcraft.io/t/unable-to-create-an-image-that-uses-private-snaps&#34;&gt;can&amp;rsquo;t preinstall those in an image&lt;/a&gt; because only you (and the collaborators you&amp;rsquo;ve added) can obtain access to it. The only way you can make an image in this case would be to make the snaps publicly available, which defeats the whole purpose of this post.&lt;/p&gt;
&lt;p&gt;For this use-case, we have what are called &lt;a href=&#34;https://ubuntu.com/core/docs/store-overview&#34;&gt;brand stores&lt;/a&gt;. Brand stores are still hosted in the Ubuntu store, but they&amp;rsquo;re a custom, curated version of it, meant to be specific to a given company or device. They can (optionally) inherit from the standard Ubuntu store, and be open to all developers or locked down to a specific group (which is what we want in our case, to keep things private).&lt;/p&gt;
&lt;p&gt;Note that this is a paid feature. You need to request a brand store by contacting Canonical. Once your request has been granted, you&amp;rsquo;ll see your new store by visiting &amp;ldquo;stores you can access&amp;rdquo; under your name.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/stores-you-can-access.jpg&#34; alt=&#34;stores you can access&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;There you&amp;rsquo;ll see the various stores to which you have access. You&amp;rsquo;ll have at least two: the normal Ubuntu store, and your new brand store. Select the brand store (red rectangle). While you&amp;rsquo;re here, record your store ID (blue rectangle): you&amp;rsquo;ll need it in a moment.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/store-list.png&#34; alt=&#34;store list&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;From there, registering names/uploading snaps works the same way, but now they go into your brand store instead of the standard one, and assuming you have it unlisted, those snaps are not available to external users. The only caveat today is that at least the first upload for the snap needs to be via the web interface. After that, you can continue to use Snapcraft like normal.&lt;/p&gt;
&lt;p&gt;So how does this change things? My &amp;ldquo;kyrofa-store&amp;rdquo; inherits snaps from the Ubuntu store, and also contains a &amp;ldquo;kyrofa-branded-test-snap&amp;rdquo; published into the stable channel. This snap isn&amp;rsquo;t available in the Ubuntu store, as you can see if you search for it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snap find kyrofa-branded
The search &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kyrofa-branded&amp;#34;&lt;/span&gt; returned &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; snaps
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But using the store ID we recorded earlier, we can make a model assertion that pulls from the brand store instead of the Ubuntu store. We just need to add the &amp;ldquo;store&amp;rdquo; key to the JSON document, making it look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;{
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;model&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;series&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;16&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;model&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;custom-amd64&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;architecture&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;amd64&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;gadget&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pc&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;kernel&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pc-kernel&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;authority-id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;4tSgWHfAL1vm9l8mSiutBDKnnSQBv0c8&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;brand-id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;4tSgWHfAL1vm9l8mSiutBDKnnSQBv0c8&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;timestamp&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2017-06-23T21:03:24+00:00&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;required-snaps&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kyrofa-branded-test-snap&amp;#34;&lt;/span&gt;],
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;store&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ky&amp;lt;secret&amp;gt;ek&amp;#34;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sign it just as we did in &lt;a href=&#34;https://kyrofa.com/posts/ubuntu-core-making-a-factory-image-with-private-snaps/#method-1-dont-put-your-snap-in-the-store-at-all&#34;&gt;Method 1&lt;/a&gt;, and we can create an Ubuntu Core image with our private, brand-store snap preinstalled as simply as:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo ubuntu-image -c stable amd64.model
Fetching core
Fetching pc-kernel
Fetching pc
Fetching kyrofa-branded-test-snap
Partition size/offset need to be a multiple of sector size &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;512&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;.
The size/offset will be rounded up to the nearest sector.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, like at the end of &lt;a href=&#34;https://kyrofa.com/posts/ubuntu-core-making-a-factory-image-with-private-snaps/#method-1-dont-put-your-snap-in-the-store-at-all&#34;&gt;Method 1&lt;/a&gt;, you have a &lt;code&gt;pc.img&lt;/code&gt; ready for the factory. However, with this method, the snaps in the image are all coming from the store, which means they will automatically update as usual.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;These are the only two options for doing this today. When I started writing this post I thought there was a third (keeping one&amp;rsquo;s snap private and creating an image with it), but that turns out to &lt;a href=&#34;https://forum.snapcraft.io/t/unable-to-create-an-image-that-uses-private-snaps&#34;&gt;not be the case&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Note that we&amp;rsquo;ve also received numerous requests for some sort of on-premises/enterprise store, and while such a product is not yet available, the store team is working on it. Once this is available, I&amp;rsquo;ll write a new post about it.&lt;/p&gt;
&lt;p&gt;I hope this proves useful!&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>How to use Snapcraft v2 on Trusty</title>
      <link>https://kyrofa.com/posts/how-to-use-snapcraft-v2-on-trusty/</link>
      <pubDate>Thu, 08 Jun 2017 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/how-to-use-snapcraft-v2-on-trusty/</guid>
      <description>As of this writing there are two major versions of Snapcraft: v1 and v2. Snapcraft v1 creates snaps that target Ubuntu Core 15, and Snapcraft v2 creates snaps that target Ubuntu Core 16. Series 16 snaps are much more widely used, being supported not only in Ubuntu Core 16 but also classic Ubuntu Xenial (desktop and server) and more recently even Trusty! It&amp;rsquo;s also supported across a range of other Linux distributions.</description>
      <content:encoded>&lt;p&gt;As of this writing there are two major versions of Snapcraft: v1 and v2. Snapcraft v1 creates snaps that target Ubuntu Core 15, and Snapcraft v2 creates snaps that target Ubuntu Core 16. Series 16 snaps are much more widely used, being supported not only in Ubuntu Core 16 but also classic Ubuntu Xenial (desktop and server) and more recently even Trusty! It&amp;rsquo;s also supported across a range of other Linux distributions. However, while series 16 snaps can run in a number of places, currently Snapcraft v2 (which makes series 16 snaps) only runs in Xenial. The Trusty archives contains Snapcraft v1. For the vast majority of cases this works fine: build your snaps on Xenial, run them anywhere. However, there are cases when you really need to run Snapcraft on Trusty. Case in point: ROS (Robot Operating System) Indigo.&lt;/p&gt;
&lt;p&gt;ROS maintains a number of releases at a time. The ones most heavily used tend to be the LTS releases: that is, the releases that correspond to the LTS releases of Ubuntu. There are two LTS versions of Ubuntu supported right now, Trusty and Xenial. The corresponding versions of ROS are Indigo and Kinetic, respectively.&lt;/p&gt;
&lt;p&gt;These are the most popular releases because switching one&amp;rsquo;s entire project to another release of ROS is not an easy task. In my experience, most teams tend to stick with a single release until it&amp;rsquo;s either no longer supported or new versions introduce features they need. Indeed, this is backed up by the &lt;a href=&#34;http://roscon.ros.org/2016/presentations/roscon-opening-remarks-2016.pdf&#34;&gt;metrics presented at ROSCon last year&lt;/a&gt;: over 70% of ROS users are still using Indigo, and about 10% are using Kinetic.&lt;/p&gt;
&lt;p&gt;This (among other things) has made making Snapcraft v2 available on Trusty a priority for us. However, due to the SRU process (i.e. the process of updating software in already-released versions of Ubuntu), it&amp;rsquo;s not as simple as just releasing an update upgrading the Snapcraft v1 already in the Trusty archives to v2. There are a lot of dependencies that are Xenial-only. The perfect solution is to start shipping Snapcraft as a snap, &lt;a href=&#34;https://forum.snapcraft.io/t/call-for-testing-snapcraft/405&#34;&gt;which we&amp;rsquo;re doing&lt;/a&gt;, but it&amp;rsquo;s still early days there. When running the Snapcraft snap on Trusty, it&amp;rsquo;s currently only intended to serve as an entry point into cleanbuild, which uses a Xenial container to build a snap. That won&amp;rsquo;t work for Indigo: we need to build on Trusty, and use Trusty&amp;rsquo;s archives for dependencies. We&amp;rsquo;re still working out the kinks to support exactly that, but until then, I know some folks who are blocked by this. If you&amp;rsquo;re part of that group, then this post is for you: I&amp;rsquo;ll walk you through running Snapcraft v2 from source on Trusty.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update: While this post may still be useful, it&amp;rsquo;s mostly historical. As of Snapcraft v2.35, the Snapcraft snap runs on Trusty. That&amp;rsquo;s not yet released as of the time of this writing, but you can install it right now from the edge channel:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install --classic --edge snapcraft
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Alright, back to your regularly-scheduled programming.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;h3 id=&#34;python-35&#34;&gt;Python 3.5&lt;/h3&gt;
&lt;p&gt;Snapcraft uses circular imports, which means it requires at least Python v3.5. Trusty&amp;rsquo;s archives have v3.4. In the Ruby community we run into this problem all the time, and have tools like &lt;a href=&#34;https://rvm.io/&#34;&gt;rvm&lt;/a&gt; to solve it. Python has a similar tool: &lt;a href=&#34;https://github.com/pyenv/pyenv&#34;&gt;pyenv&lt;/a&gt;. We&amp;rsquo;ll use it to get Python v3.5. First, you need git:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt install git
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you can use the &lt;code&gt;pyenv&lt;/code&gt; installer to get up and running (it places &lt;code&gt;pyenv&lt;/code&gt; into &lt;code&gt;~/.pyenv&lt;/code&gt;):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ curl -L https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When that has finished, you&amp;rsquo;ll see it recommending you place a few lines into &lt;code&gt;~/.bash_profile&lt;/code&gt;. I just placed those lines at the end of my &lt;code&gt;~/.bashrc&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;export PATH&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/home/ubuntu/.pyenv/bin:&lt;/span&gt;$PATH&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
eval &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;pyenv init -&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
eval &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;pyenv virtualenv-init -&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now either start a new terminal session or simply re-source it, and verify that it works by running an update (which should do nothing other than say stuff is up-to-date):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ source ~/.bashrc
$ pyenv update
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;pyenv&lt;/code&gt; will build Python, for which you&amp;rsquo;ll need a few dependencies:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt install gcc make libssl-dev libbz2-dev libreadline-dev liblzma-dev libsqlite3-dev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we&amp;rsquo;re ready to build Python v3.5, but we have a small issue. &lt;code&gt;pyenv&lt;/code&gt; will build upstream Python, which doesn&amp;rsquo;t actually detect Ubuntu correctly. Snapcraft does different things depending on the OS, so Ubuntu needs to be detected correctly. Fortunately there&amp;rsquo;s a distro patch for that, so let&amp;rsquo;s just use it. You&amp;rsquo;ll need a few tools:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt install bzr patchutils
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now fetch the Ubuntu source for Python v3.5:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ bzr branch lp:~doko/python/pkg3.5-debian
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Alright, now let&amp;rsquo;s get Python v3.5 (specifically v3.5.2, which is what&amp;rsquo;s currently in Xenial), and tell &lt;code&gt;pyenv&lt;/code&gt; to apply that distro patch before building:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cat pkg3.5-debian/patches/platform-lsbrelease.diff | filterdiff --strip&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; | pyenv install -p 3.5.2
Downloading Python-3.5.2.tar.xz...
-&amp;gt; https://www.python.org/ftp/python/3.5.2/Python-3.5.2.tar.xz
Installing Python-3.5.2...
patching file Lib/platform.py
Hunk &lt;span style=&#34;color:#75715e&#34;&gt;#1 succeeded at 268 (offset 2 lines).&lt;/span&gt;
Hunk &lt;span style=&#34;color:#75715e&#34;&gt;#2 succeeded at 297 (offset 2 lines).&lt;/span&gt;
Hunk &lt;span style=&#34;color:#75715e&#34;&gt;#3 succeeded at 333 (offset 1 line).&lt;/span&gt;
patching file Lib/test/test_platform.py
Hunk &lt;span style=&#34;color:#75715e&#34;&gt;#1 succeeded at 320 (offset 16 lines).&lt;/span&gt;
Installed Python-3.5.2 to /home/ubuntu/.pyenv/versions/3.5.2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you can see that you have multiple Python versions available (with the current one represented by the asterisk):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ pyenv versions
* system &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;set by /home/ubuntu/.pyenv/version&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
 3.5.2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s go ahead and change the active one to 3.5.2:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ pyenv global 3.5.2
$ pyenv versions
 system
* 3.5.2 &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;set by /home/ubuntu/.pyenv/version&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
$ python3 --version
Python 3.5.2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we&amp;rsquo;re using v3.5.2. You can set it back later by using &lt;code&gt;pyenv global system&lt;/code&gt; if you like.&lt;/p&gt;
&lt;h3 id=&#34;libsodium&#34;&gt;libsodium&lt;/h3&gt;
&lt;p&gt;Snapcraft depends on &lt;code&gt;libsodium&lt;/code&gt; for communication with the store, but it&amp;rsquo;s only in the Xenial archives, not Trusty&amp;rsquo;s. Thankfully there&amp;rsquo;s a PPA that provides it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo add-apt-repository ppa:chris-lea/libsodium
$ sudo apt update
$ sudo apt install libsodium-dev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;install-snapcraft&#34;&gt;Install Snapcraft&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;re going to install Snapcraft via &lt;code&gt;pip&lt;/code&gt;, as outlined in its &lt;a href=&#34;https://github.com/snapcore/snapcraft/blob/3.5/HACKING.md&#34;&gt;hacking guide&lt;/a&gt;. Doing that on Trusty requires a few tweaks, so use my special &lt;code&gt;trusty-support&lt;/code&gt; branch:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ git clone -b trusty-support https://github.com/kyrofa/snapcraft
Cloning into &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;snapcraft&amp;#39;&lt;/span&gt;...
remote: Counting objects: 18235, &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;.
remote: Compressing objects: 100% &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;26/26&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;.
remote: Total &lt;span style=&#34;color:#ae81ff&#34;&gt;18235&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;delta 9&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;, reused &lt;span style=&#34;color:#ae81ff&#34;&gt;13&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;delta 5&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;, pack-reused &lt;span style=&#34;color:#ae81ff&#34;&gt;18204&lt;/span&gt;
Receiving objects: 100% &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;18235/18235&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;, 6.84 MiB | 6.46 MiB/s, &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;.
Resolving deltas: 100% &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;12342/12342&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;.
Checking connectivity... &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now install its dependencies (taken from the hacking guide):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt install g++ libffi-dev libapt-pkg-dev libarchive13
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As far as how/where to actually install Snapcraft, that&amp;rsquo;s up to you. Personally, I like to use virtualenv so I don&amp;rsquo;t clutter my system. Pretty much exactly as outlined in the hacking guide (guess who wrote that bit):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ mkdir ~/venv
$ python3 -m venv ~/venv/snapcraft
$ source ~/venv/snapcraft/bin/activate
&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;snapcraft&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; $ pip install --upgrade pip
&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;snapcraft&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; $ pip install -r requirements.txt .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As long as you&amp;rsquo;re within that virtual environment (by sourcing its &lt;code&gt;activate&lt;/code&gt; script) you&amp;rsquo;ll have Snapcraft v2 available to you! Now you can go &lt;a href=&#34;https://snapcraft.io/&#34;&gt;build your snaps like normal&lt;/a&gt;, but you&amp;rsquo;re running on Trusty now, using the Trusty archives for stage-packages, etc. I&amp;rsquo;ve tested this specifically for ROS Indigo, but of course other things should work as well unless there&amp;rsquo;s a hard dependency on Xenial. I hope this is helpful until the snap fully supports Trusty!&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Continuous acceptance tests for complex applications</title>
      <link>https://kyrofa.com/posts/continuous-acceptance-tests-for-complex-applications/</link>
      <pubDate>Mon, 05 Jun 2017 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/continuous-acceptance-tests-for-complex-applications/</guid>
      <description>So you&amp;rsquo;re developing a complex application. Maybe it has some really specific dependencies, or requires a lot of setup. In many cases (such as my own), it&amp;rsquo;s a web application. I have a suite of tests, varying all the way from unit tests through integration tests. The latter typically uses Selenium, and I often integrate it with Sauce Labs. I&amp;rsquo;ve written an article about this before. However, even those integration tests aren&amp;rsquo;t testing the real application: it&amp;rsquo;s not running against a production database, it&amp;rsquo;s not running with a production web server, and so on.</description>
      <content:encoded>&lt;p&gt;So you&amp;rsquo;re developing a complex application. Maybe it has some really specific dependencies, or requires a lot of setup. In many cases (such as my own), it&amp;rsquo;s a web application. I have a suite of tests, varying all the way from unit tests through integration tests. The latter typically uses Selenium, and I often integrate it with Sauce Labs. I&amp;rsquo;ve written &lt;a href=&#34;https://kyrofa.com/posts/travis-ci-and-sauce-labs&#34;&gt;an article&lt;/a&gt; about this before. However, even those integration tests aren&amp;rsquo;t testing the real application: it&amp;rsquo;s not running against a production database, it&amp;rsquo;s not running with a production web server, and so on. Doing this is not easy; oftentimes the solutions I see look like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create custom docker images with the rest of the system (dependencies, database, web server, etc.) setup correctly&lt;/li&gt;
&lt;li&gt;Inject the code into it&lt;/li&gt;
&lt;li&gt;Run tests against it&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Not only this, but one needs to ensure one&amp;rsquo;s image stays in line with one&amp;rsquo;s actual dependencies and continues to mirror a production deployment.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a better way: package your application as a snap.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve &lt;a href=&#34;https://kyrofa.com/posts/installing-nextcloud-can-be-a-snap&#34;&gt;written about&lt;/a&gt; the Nextcloud snap a number of times. It&amp;rsquo;s one of the more complex snaps out there, bundling Apache, MySQL, PHP-FPM, etc. It&amp;rsquo;s a production-ready snap, following Nextcloud&amp;rsquo;s recommended settings. This makes it a perfect testing target! Except&amp;hellip; there are no hosted CI options that can actually run snaps. All the ones I tried didn&amp;rsquo;t have a kernel configured properly for snaps (e.g. Travis). I was starting to investigate integrating my private GitLab instance (with its own amazing CI) with GitHub just to run tests against the snap.&lt;/p&gt;
&lt;p&gt;Then I heard about Circle CI. By default they run docker on Ubuntu Trusty, but they have actual VMs available as a beta (the &lt;code&gt;machine&lt;/code&gt; executor), which have a Xenial kernel and can actually run snaps! It&amp;rsquo;s a bit limited, as I&amp;rsquo;ll discuss, but it solves a real need at least for the Nextcloud snap, and I&amp;rsquo;d like to walk you through doing the same thing.&lt;/p&gt;
&lt;h2 id=&#34;backstory&#34;&gt;Backstory&lt;/h2&gt;
&lt;p&gt;The Nextcloud snap publishes on a &lt;a href=&#34;https://github.com/nextcloud/nextcloud-snap/wiki/Release-strategy&#34;&gt;number of different channels&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;stable&lt;/code&gt;: The current stable release of Nextcloud (currently v11.0.3. Yes, I know v12 is out, we&amp;rsquo;ll talk about that in a sec)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;candidate&lt;/code&gt;: Release candidates, used for testing calls before releasing to &lt;code&gt;stable&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;beta&lt;/code&gt;: Snap development branch. Automatically built as new features are added to the snap (e.g. when pull requests are merged for the snap)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;edge&lt;/code&gt;: Daily builds of Nextcloud&amp;rsquo;s master branch&lt;/li&gt;
&lt;li&gt;&lt;code&gt;11/edge&lt;/code&gt;: Daily builds of Nextcloud&amp;rsquo;s v11 maintenance branch&lt;/li&gt;
&lt;li&gt;&lt;code&gt;12/edge&lt;/code&gt;: Daily builds of Nextcloud&amp;rsquo;s v12 maintenance branch&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Any one of these could immediately be installed with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install nextcloud --channel&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&amp;lt;channel&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s actually pretty neat: want to see what work went into Nextcloud yesterday? &lt;code&gt;snap install --edge nextcloud&lt;/code&gt;. It&amp;rsquo;ll automatically update daily, giving you the opportunity to easily track development.&lt;/p&gt;
&lt;p&gt;We have all these things happening automatically, but with &lt;em&gt;zero CI&lt;/em&gt; because as I mentioned, we couldn&amp;rsquo;t find any hosted CI solutions that were shiny enough to actually run snaps. Of course we didn&amp;rsquo;t release to &lt;code&gt;beta&lt;/code&gt; much less &lt;code&gt;candidate&lt;/code&gt; or &lt;code&gt;stable&lt;/code&gt; without some extensive manual testing, but it wasn&amp;rsquo;t good enough: &lt;em&gt;no one was testing &lt;code&gt;edge&lt;/code&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;So Nextcloud version 12 was just released upstream, and we quickly discovered that it &lt;a href=&#34;https://github.com/nextcloud/nextcloud-snap/issues/280&#34;&gt;doesn&amp;rsquo;t work in the snap&lt;/a&gt;. If we were actually able to run tests against the snap we could have caught this before v12 was actually released!&lt;/p&gt;
&lt;h2 id=&#34;the-solution&#34;&gt;The solution&lt;/h2&gt;
&lt;p&gt;A few weeks ago, I saw a forum post from Alan detailing his &lt;a href=&#34;https://forum.snapcraft.io/t/building-and-pushing-snaps-using-circleci&#34;&gt;use of Circle CI&lt;/a&gt; for building/pushing snaps. Our builds are already automated so I wasn&amp;rsquo;t so interested in that, but what caught my eye was the fact that he was installing Snapcraft &lt;em&gt;from the snap&lt;/em&gt;, i.e. Circle CI could run snaps! I took a closer look as soon as I could.&lt;/p&gt;
&lt;p&gt;It turns out that, like Travis, Circle CI runs on Ubuntu 14.04 (Trusty). Snaps actually can run on Trusty, but the kernel was too old. However, instead of using Docker as an executor type, one can use the (still-in-beta) &lt;a href=&#34;https://circleci.com/docs/2.0/executor-types/&#34;&gt;&lt;code&gt;machine&lt;/code&gt; executor type&lt;/a&gt;, which spins up a VM that is still based on Trusty, but uses a newer v4.4 kernel.&lt;/p&gt;
&lt;p&gt;Once I enabled the Circle CI integration for the project, it was a simple matter of actually creating acceptance tests to run, and then creating the correct Circle CI configuration to build the snap and run the tests against it.&lt;/p&gt;
&lt;h3 id=&#34;creating-acceptance-tests&#34;&gt;Creating acceptance tests&lt;/h3&gt;
&lt;p&gt;Nextcloud actually already has a &lt;a href=&#34;https://github.com/nextcloud/server/tree/master/tests/acceptance&#34;&gt;suite of acceptance tests&lt;/a&gt;. However, they make a lot of assumptions about where they are and how they&amp;rsquo;re run, such that they wouldn&amp;rsquo;t work for our purposes.&lt;/p&gt;
&lt;p&gt;I have a lot of experience writing Rails apps, and one of my favorite tools from that world is Capybara. I&amp;rsquo;ve never used it in a Ruby-only project, but it was a perfect fit for what I needed to do, so I went for it. I needed five gems&amp;ndash; here&amp;rsquo;s my Gemfile:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;source &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://rubygems.org&amp;#39;&lt;/span&gt;

&lt;span style=&#34;color:#75715e&#34;&gt;# Test driver&lt;/span&gt;
gem &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;capybara&amp;#39;&lt;/span&gt;

&lt;span style=&#34;color:#75715e&#34;&gt;# Webdriver (for javascript)&lt;/span&gt;
gem &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;capybara-webkit&amp;#39;&lt;/span&gt;

&lt;span style=&#34;color:#75715e&#34;&gt;# Create xvfb from ruby (needed by webkit)&lt;/span&gt;
gem &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;headless&amp;#39;&lt;/span&gt;

&lt;span style=&#34;color:#75715e&#34;&gt;# Use nice acceptance tests DSL&lt;/span&gt;
gem &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;rspec&amp;#39;&lt;/span&gt;

&lt;span style=&#34;color:#75715e&#34;&gt;# Run tests with a decent CLI&lt;/span&gt;
gem &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;rake&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Install those gems and their dependencies:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt install gcc g++ make qt5-default libqt5webkit5-dev ruby-dev zlib1g-dev
$ gem install bundler
$ bundle install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then setup rspec:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ rspec --init
 create  .rspec
 create  spec/spec_helper.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That &lt;code&gt;spec/spec_helper.rb&lt;/code&gt; is where we put some setup code for the tests:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;require &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;capybara&amp;#39;&lt;/span&gt;
require &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;capybara/dsl&amp;#39;&lt;/span&gt;
require &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;capybara/rspec&amp;#39;&lt;/span&gt;
require &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;capybara-webkit&amp;#39;&lt;/span&gt;
require &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;headless&amp;#39;&lt;/span&gt;

&lt;span style=&#34;color:#66d9ef&#34;&gt;include&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;Capybara&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;DSL&lt;/span&gt;

&lt;span style=&#34;color:#66d9ef&#34;&gt;Capybara&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;configure &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; config &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;
   &lt;span style=&#34;color:#75715e&#34;&gt;# Set javascript driver to webkit (selenium is the default)&lt;/span&gt;
   config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;default_driver &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;:webkit&lt;/span&gt;

   &lt;span style=&#34;color:#75715e&#34;&gt;# Assume the snap is available at localhost. Change here if otherwise.&lt;/span&gt;
   config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;app_host &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;http://localhost&amp;#39;&lt;/span&gt;

   &lt;span style=&#34;color:#75715e&#34;&gt;# The snap will already be running, no need to run a server.&lt;/span&gt;
   config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;run_server &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;

&lt;span style=&#34;color:#66d9ef&#34;&gt;Capybara&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;Webkit&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;configure &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;config&lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;
   &lt;span style=&#34;color:#75715e&#34;&gt;# Don&amp;#39;t raise errors when SSL certificates can&amp;#39;t be validated&lt;/span&gt;
   config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ignore_ssl_errors

   &lt;span style=&#34;color:#75715e&#34;&gt;# Raise JavaScript errors as exceptions&lt;/span&gt;
   config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;raise_javascript_errors &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;

   &lt;span style=&#34;color:#75715e&#34;&gt;# Allow pages to make requests to any URL without issuing a warning.&lt;/span&gt;
   config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;allow_unknown_urls
&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;

&lt;span style=&#34;color:#66d9ef&#34;&gt;RSpec&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;configure &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;config&lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;
   config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;expect_with &lt;span style=&#34;color:#e6db74&#34;&gt;:rspec&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;expectations&lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;
       expectations&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;include_chain_clauses_in_custom_matcher_descriptions &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
   &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;

   config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;mock_with &lt;span style=&#34;color:#e6db74&#34;&gt;:rspec&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;mocks&lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;
       mocks&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;verify_partial_doubles &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
   &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;

   config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;shared_context_metadata_behavior &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;:apply_to_host_groups&lt;/span&gt;

   &lt;span style=&#34;color:#75715e&#34;&gt;# Webkit needs a framebuffer in order to work, so create one before the tests&lt;/span&gt;
   &lt;span style=&#34;color:#75715e&#34;&gt;# run, and destroy it afterward.&lt;/span&gt;
   config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;before(&lt;span style=&#34;color:#e6db74&#34;&gt;:all&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
       @headless &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;Headless&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;new
       @headless&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start
   &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;

   config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;after(&lt;span style=&#34;color:#e6db74&#34;&gt;:all&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
       @headless&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;destroy
   &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With the generic setup out of the way, I decided to write a simple test that would have caught the issue introduced in v12: just login with and without valid credentials (this test assumes the existence of an &amp;ldquo;admin&amp;rdquo; user whose password is &amp;ldquo;admin&amp;rdquo;).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;feature &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Logging in&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
   scenario &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Logging in with correct credentials&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
       visit &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;
       fill_in &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;User&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;with&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;admin&amp;#34;&lt;/span&gt;
       fill_in &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Password&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;with&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;admin&amp;#34;&lt;/span&gt;
       click_button &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Log in&amp;#34;&lt;/span&gt;
       expect(page)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;to have_content &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Documents&amp;#34;&lt;/span&gt;
   &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;

   scenario &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Logging in with incorrect credentials&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
       visit &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;
       fill_in &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;User&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;with&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;wronguser&amp;#34;&lt;/span&gt;
       fill_in &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Password&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;with&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;wrongpassword&amp;#34;&lt;/span&gt;
       click_button &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Log in&amp;#34;&lt;/span&gt;
       expect(page)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;to have_content &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Wrong password&amp;#34;&lt;/span&gt;
   &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Short and sweet. In order to run this test, I created a &lt;code&gt;Rakefile&lt;/code&gt; in the directory containing the &lt;code&gt;spec/&lt;/code&gt; folder, with the following contents:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;require &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;rake&amp;#39;&lt;/span&gt;
require &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;rspec/core/rake_task&amp;#39;&lt;/span&gt;

&lt;span style=&#34;color:#66d9ef&#34;&gt;RSpec&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;Core&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;RakeTask&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;new(&lt;span style=&#34;color:#e6db74&#34;&gt;:test&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;t&lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;
   t&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;pattern &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;Dir&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;glob(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;spec/**/*_spec.rb&amp;#39;&lt;/span&gt;)
&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;

task &lt;span style=&#34;color:#e6db74&#34;&gt;:default&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;:test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now the entire suite of tests (even when more specs are added) can be run with &lt;code&gt;rake test&lt;/code&gt;. I verified that these tests pass normally, and throw terrible javascript errors in v12, so this simple suite would indeed catch the issue that bit us. Time to get it into CI!&lt;/p&gt;
&lt;h3 id=&#34;circle-ci-config&#34;&gt;Circle CI Config&lt;/h3&gt;
&lt;p&gt;Since I needed to use that new executor type, I needed to use &lt;a href=&#34;https://circleci.com/docs/2.0/&#34;&gt;Circle CI 2.0&lt;/a&gt;. Here&amp;rsquo;s the YAML I ended up using:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;jobs&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;build&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;working_directory&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;~/nextcloud-snap&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;machine&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;steps&lt;/span&gt;:
     - &lt;span style=&#34;color:#ae81ff&#34;&gt;checkout&lt;/span&gt;
     - &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;:
         &lt;span style=&#34;color:#f92672&#34;&gt;command&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;           sudo apt update
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;           sudo apt install -y snapd
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;           docker run -v $(pwd):$(pwd) -t ubuntu:xenial sh -c &amp;#34;apt update -qq &amp;amp;&amp;amp; apt install snapcraft -y &amp;amp;&amp;amp; cd $(pwd) &amp;amp;&amp;amp; snapcraft&amp;#34;&lt;/span&gt;           
     - &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;:
         &lt;span style=&#34;color:#f92672&#34;&gt;command&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;           sudo snap install *.snap --dangerous
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;           sudo apt install gcc g++ make qt5-default libqt5webkit5-dev ruby-dev zlib1g-dev -y
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;           sudo gem install bundle
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;           cd tests
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;           bundle install --deployment
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;           sudo nextcloud.manual-install admin admin
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;           bundle exec rake test&lt;/span&gt;           
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s beyond the scope of this post to teach the entire Circle CI syntax, but I want to go over the important parts in more detail: the two &lt;code&gt;run&lt;/code&gt; steps (they&amp;rsquo;re run in order).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;command&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;     sudo apt update
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;     sudo apt install -y snapd
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;     docker run -v $(pwd):$(pwd) -t ubuntu:xenial sh -c &amp;#34;apt update -qq &amp;amp;&amp;amp; apt install snapcraft -y &amp;amp;&amp;amp; cd $(pwd) &amp;amp;&amp;amp; snapcraft&amp;#34;&lt;/span&gt;     
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As I mentioned, the &lt;code&gt;machine&lt;/code&gt; executor type is Trusty. Well, the Nextcloud snap depends on packages in the Xenial archives, so it needs to build on Xenial. So we fire up a Xenial Docker image just to build the snap. We mount the current working directory into place so that the built snap ends up on the host once complete.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;command&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;     sudo snap install *.snap --dangerous
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;     sudo apt install gcc g++ make qt5-default libqt5webkit5-dev ruby-dev zlib1g-dev -y
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;     sudo gem install bundle
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;     cd tests
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;     bundle install --deployment
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;     sudo nextcloud.manual-install admin admin
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;     bundle exec rake test&lt;/span&gt;     
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This step runs after the one that builds the snap, so the first thing we do is install the built snap. This works because as I said, snaps run on Trusty (given the right kernel). We then install the prerequisites for our test suite, create an &amp;ldquo;admin&amp;rdquo; user with its password set to &amp;ldquo;admin&amp;rdquo;, and then run the tests.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Building, installing/running, and testing the complete, complex snap is awesome. We don&amp;rsquo;t have to worry about any differing dependencies between production and testbed (because they&amp;rsquo;re literally the same binary), and Capybara works wonderfully well standalone in this manner. I&amp;rsquo;m really thankful for Circle CI actually supporting running snaps, particularly after hitting a wall with e.g. Travis.&lt;/p&gt;
&lt;p&gt;Right now the snap is built and tested for every pull request in the snap. We need to start running these tests daily against the daily snaps as well, but unlike Travis (which recently introduced cron jobs), Circle CI does not natively support running jobs periodically. It does support triggering jobs via a web API though, so I may just trigger it from cron running on my own infrastructure. Beyond that, I look forward to fleshing the test suite out further. If you have good ideas, make a pull request! We&amp;rsquo;d love to see what you have in mind.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Call for testing: Nextcloud snap v11.0.3snap3</title>
      <link>https://kyrofa.com/posts/call-for-testing-nextcloud-snap-v11-0-3snap3/</link>
      <pubDate>Wed, 24 May 2017 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/call-for-testing-nextcloud-snap-v11-0-3snap3/</guid>
      <description>We&amp;rsquo;re getting ready to release a new version of the Nextcloud snap. v11.0.3snap3-rc1 is now in the candidate channel:
$ sudo snap install --candidate nextcloud This update includes:
 An update to MySQL v5.7.18 An update to certbot v0.14.1  We&amp;rsquo;d love this new version (particularly features that involve those updates) to get some exercise before we release it. Feel like helping? We&amp;rsquo;ve put together some smoke tests to get you started.</description>
      <content:encoded>&lt;p&gt;We&amp;rsquo;re &lt;a href=&#34;http://bit.ly/2qQ7MRG&#34;&gt;getting ready to release&lt;/a&gt; a new version of the Nextcloud snap. &lt;code&gt;v11.0.3snap3-rc1&lt;/code&gt; is now in the &lt;code&gt;candidate&lt;/code&gt; channel:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install --candidate nextcloud
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This update includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An update to MySQL v5.7.18&lt;/li&gt;
&lt;li&gt;An update to certbot v0.14.1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We&amp;rsquo;d love this new version (particularly features that involve those updates) to get some exercise before we release it. Feel like helping? We&amp;rsquo;ve put together some &lt;a href=&#34;https://github.com/nextcloud/nextcloud-snap/wiki/Smoke-Tests&#34;&gt;smoke tests&lt;/a&gt; to get you started.&lt;/p&gt;
&lt;p&gt;Please test all you can, but if you only have time to focus on the smoke tests covering the new features, that would still be very useful. We&amp;rsquo;re particularly looking to cover that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There are no regressions regarding obtaining certificates from Let&amp;rsquo;s Encrypt&lt;/li&gt;
&lt;li&gt;There are no issues upgrading from the current Nextcloud snap to the new one with the new version of MySQL.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thank you for your help, and let me know if you have any questions.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>ROS production: behind the scenes</title>
      <link>https://kyrofa.com/posts/ros-production-behind-the-scenes/</link>
      <pubDate>Fri, 19 May 2017 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/ros-production-behind-the-scenes/</guid>
      <description>I&amp;rsquo;m an engineer, and am pretty clueless when it comes to film-making. However, I&amp;rsquo;ve been asked by a few different people how I made the ROS prototype to production on Ubuntu Core video series. While I&amp;rsquo;m not deluding myself into thinking I&amp;rsquo;m a pro, I&amp;rsquo;m happy with how they turned out, and they were made on a shoestring budget with open-source software. This post is for those who are curious about what I did, what I used, and how I did it.</description>
      <content:encoded>&lt;p&gt;I&amp;rsquo;m an engineer, and am pretty clueless when it comes to film-making. However, I&amp;rsquo;ve been asked by a few different people how I made the &lt;a href=&#34;https://kyrofa.com/posts/from-ros-prototype-to-production-on-ubuntu-core-1-5&#34;&gt;ROS prototype to production on Ubuntu Core&lt;/a&gt; video series. While I&amp;rsquo;m not deluding myself into thinking I&amp;rsquo;m a pro, I&amp;rsquo;m happy with how they turned out, and they were made on a shoestring budget with open-source software. This post is for those who are curious about what I did, what I used, and how I did it.&lt;/p&gt;
&lt;h2 id=&#34;the-hardware&#34;&gt;The hardware&lt;/h2&gt;
&lt;h3 id=&#34;the-camera&#34;&gt;The camera&lt;/h3&gt;
&lt;p&gt;I don&amp;rsquo;t care how awesome you are at post-processing, there&amp;rsquo;s not a lot you can do to make up for a terrible camera. For this series, I used a Canon Rebel T5i, which is pretty good (I wouldn&amp;rsquo;t call it cheap, but it&amp;rsquo;s far from top-of-the-line).&lt;/p&gt;
&lt;p&gt;I already owned it, but if you have the opportunity to buy a camera, here are some things to consider:&lt;/p&gt;
&lt;h4 id=&#34;resolution&#34;&gt;Resolution&lt;/h4&gt;
&lt;p&gt;The T5i can only shoot video in 1080p. Perhaps you&amp;rsquo;re wanting to make 4k video: you can stop right here. But perhaps you&amp;rsquo;re happy with making 1080p video: this may work fine, but you won&amp;rsquo;t be able to punch into the image at all without degrading quality (e.g. create footage like &lt;a href=&#34;https://youtu.be/tajEsY032-8&#34;&gt;this&lt;/a&gt;).&lt;/p&gt;
&lt;h4 id=&#34;frame-rate&#34;&gt;Frame rate&lt;/h4&gt;
&lt;p&gt;At 1080p, the T5i maxes out at 30fps (you can get a bit more if you shoot at a lower resolution, but who wants that?). The videos I produce are 24fps, so that&amp;rsquo;s acceptable. But it means I can&amp;rsquo;t move very quickly without unacceptable blur, and I can&amp;rsquo;t do slow motion at all without it looking jumpy. You&amp;rsquo;ll notice that I do, though, and it doesn&amp;rsquo;t look jumpy. I faked it: the robot was barely moving. The &amp;ldquo;slow motion&amp;rdquo; was really full-speed, and the faster stuff was sped up. Yeah, I recommend a higher-frame-rate camera if you&amp;rsquo;re able to swing it (assuming you love slow-mo vibes as much as I do).&lt;/p&gt;
&lt;h4 id=&#34;formats&#34;&gt;Formats&lt;/h4&gt;
&lt;p&gt;When I shoot photos I shoot in RAW, and edit them in &lt;a href=&#34;https://www.darktable.org/&#34;&gt;Darktable&lt;/a&gt;. When I purchased the camera that&amp;rsquo;s really all I wanted, but now that I&amp;rsquo;m making video with it I&amp;rsquo;m saddened by the fact that, when making video, it only supports h264. I think I could get more out of it if I used &lt;a href=&#34;http://www.magiclantern.fm/&#34;&gt;Magic Lantern&lt;/a&gt;, but just because I run Linux doesn&amp;rsquo;t mean I want to tinker with everything (there&amp;rsquo;s a reason I use Ubuntu). Perhaps I&amp;rsquo;ll get there eventually, but until then, the alterations I can make while editing are somewhat limited.&lt;/p&gt;
&lt;h3 id=&#34;the-microphone&#34;&gt;The microphone&lt;/h3&gt;
&lt;p&gt;Camera microphones are even worse than the microphones built into your laptop. If you actually need audio while you&amp;rsquo;re taking the video (e.g. recording an interview), I recommend something like the &lt;a href=&#34;https://www.amazon.com/Rode-NTG4-Shotgun-Microphone/dp/B00SQD8DDY/ref=sr_1_2?s=photo&amp;amp;ie=UTF8&amp;amp;qid=1495069435&amp;amp;sr=1-2&amp;amp;keywords=ntg4&#34;&gt;Rode NTG4&lt;/a&gt;. I&amp;rsquo;d love one of those. Fortunately, I had no need to use audio from the video. I could record the footage and then voice over it later. I actually do voice work for radio, so I happen to already have a good mic (an &lt;a href=&#34;https://www.amazon.com/gp/product/B001CJ45KY/ref=oh_aui_search_detailpage?ie=UTF8&amp;amp;psc=1&#34;&gt;MXL 009&lt;/a&gt;, to be specific).&lt;/p&gt;
&lt;h3 id=&#34;lighting&#34;&gt;Lighting&lt;/h3&gt;
&lt;p&gt;I shot all the footage for the series in my living room and kitchen (had to kick the family out every time, they loved that). There are some big windows in there and a good amount of daylight, but I was shooting toward the middle of the house, and the light just wasn&amp;rsquo;t good enough to shoot at the ISO I wanted (800 or less), so I needed some lights. However, I liked how the light coming from the windows looked, so I wanted to make sure I kept that appearance. I did some research, and decided I wanted a soft box. Two, actually: a key light, and a fill light (to soften the shadows made by the key light). I ran into a few issues here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The soft boxes themselves are not that expensive, but the lights that go inside them can be. I was probably looking at a few hundred bucks (at least).&lt;/li&gt;
&lt;li&gt;Even if Canonical was okay with that cost (I doubted they would be), I needed to order them and I was on a deadline (anyone else notice that Amazon&amp;rsquo;s two-day shipping == five-day shipping these days?)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So I went with an alternative.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/softboxes.jpg&#34; alt=&#34;softboxes&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;What you see here is a cheap styrofoam cooler covered in parchment paper to diffuse the light. I poked a work lamp through the back of it and put a daylight (5700K) LED bulb in there. I made two of them. Not durable in the least but they worked well for this project and still exist, so apparently durable enough.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I selected LED bulbs because I didn&amp;rsquo;t want to put anything hot inside styrofoam, but if you&amp;rsquo;re using a fast shutter speed, beware of flicker. I was using commercial LED bulbs, and they flickered quite a bit if I went too high.&lt;/p&gt;
&lt;p&gt;Often a three-point light system is used that includes a back light, but I found the light coming from the windows worked well enough for that. How I positioned the lights varied depending on the shot I was trying to get, but most of the time it looked something like this (the vertical lines are the windows):&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/lighting-system.jpg&#34; alt=&#34;lighting system&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;The most difficult parts were the moving shots, where I really didn&amp;rsquo;t want to cast a shadow by crossing the light and the subject. Those shots were obtained by rotating the light setup a bit, or putting lights on chairs, etc. I never moved the lights while shooting to keep it looking like the light was coming from windows.&lt;/p&gt;
&lt;h3 id=&#34;slider&#34;&gt;Slider&lt;/h3&gt;
&lt;p&gt;Lots of the footage I captured was in motion, with most of it at floor-level. I needed to move slowly and smoothly, which meant some sort of slider/dolly track. Those can start getting up there in price, and again, I had no time. I really wanted to &lt;a href=&#34;https://youtu.be/GE37xI14Fyw&#34;&gt;make one of these&lt;/a&gt;, but decided I didn&amp;rsquo;t have time for that either. So again, I went with an alternative.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/dolly.jpg&#34; alt=&#34;dolly&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;Even this I had to get on loan from my two year old.&lt;/p&gt;
&lt;p&gt;My wooden floors have a texture to them that make them lovely to look at and walk on, but dreadful for moving my professional-grade slider smoothly, so my yoga mat is there to even it out.&lt;/p&gt;
&lt;p&gt;This setup worked for the situations when I was shooting alongside the action (e.g. the intro for &lt;a href=&#34;https://youtu.be/x6BkzfwOZbc?list=PL1LO5F1-Jh8K6R1Ba23irALtey6CRFFYu&#34;&gt;part 1&lt;/a&gt;), but there were other times when I needed to move straight toward the action, when the yoga mat would be in the way. Also, the Hess 2016 Edition does not have a nice way to mount the camera facing forward. I should complain. Regardless, when moving along the rough floor I found painted (i.e. slick) cardboard worked acceptably well.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll see footage captured from that in the intro for &lt;a href=&#34;https://youtu.be/x6BkzfwOZbc?list=PL1LO5F1-Jh8K6R1Ba23irALtey6CRFFYu&#34;&gt;part 1&lt;/a&gt; as well as &lt;a href=&#34;https://youtu.be/QFrOXd2avfo?list=PL1LO5F1-Jh8K6R1Ba23irALtey6CRFFYu&#34;&gt;part 2&lt;/a&gt;. The moving footage well above the floor was done via a tripod set on this same piece of cardboard. A fluid head would have been handy.&lt;/p&gt;
&lt;p&gt;In the intro for &lt;a href=&#34;https://youtu.be/NUackegFqWQ?list=PL1LO5F1-Jh8K6R1Ba23irALtey6CRFFYu&#34;&gt;part 5&lt;/a&gt; I needed to get some moving footage where I was front-facing and tilted downward, very close to the surface of my kitchen counter. The mount situation on the Hess bit me here, so I had to break out the Adventure Wheels.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/adventure-wheels.jpg&#34; alt=&#34;adventure wheels&#34;  /&gt;
&lt;/p&gt;
&lt;h2 id=&#34;the-software&#34;&gt;The software&lt;/h2&gt;
&lt;h3 id=&#34;capturing-my-screen&#34;&gt;Capturing my screen&lt;/h3&gt;
&lt;p&gt;Man, I&amp;rsquo;ll tell you. The screen recording software situation on Linux is so sad. I would say it&amp;rsquo;s like a barren wasteland, except that there are &lt;em&gt;several&lt;/em&gt; options. All of which suck. I think I tried them all. Every one of them either couldn&amp;rsquo;t handle my 4k screen (recording only the upper-left quarter of it) or the color was way off.&lt;/p&gt;
&lt;p&gt;The least bad option was &lt;a href=&#34;http://recordmydesktop.sourceforge.net/about.php&#34;&gt;RecordMyDesktop&lt;/a&gt;. It was the one exception to the previous broad brush: it could handle 4k and the colors looked okay. It was insanely slow and was only happy recording at 15fps (I tried switching to 24 and it ended up a garbled mess), and even then a bunch of frames got encoded out of order. Watch some of my videos closely: that wasn&amp;rsquo;t a typo that got deleted and re-typed, it was RecordMyDesktop shuffling frames randomly. But it did the job well enough.&lt;/p&gt;
&lt;p&gt;You might be asking: &amp;ldquo;Why didn&amp;rsquo;t you just bump your resolution down to 1920x1080 to record?&amp;rdquo; First of all, I shouldn&amp;rsquo;t have to. Really though, it&amp;rsquo;s because I wanted to be able to zoom into portions of the video without losing quality (e.g. step 1 of the video for &lt;a href=&#34;https://youtu.be/m9wqj34-2go?list=PL1LO5F1-Jh8K6R1Ba23irALtey6CRFFYu&#34;&gt;part 4&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Most of my recordings were of full-screen applications: I&amp;rsquo;d make them full-screen (which hid the clock and task bar), make the text larger so things would be readable on a smaller video, select that window within RecordMyDesktop, and start recording.&lt;/p&gt;
&lt;p&gt;I did things in the smallest chunks possible, because RecordMyDesktop would take ages to encode. For example, if I needed to show three separate commands in the terminal one after the other, I&amp;rsquo;d record each of them in separate takes, simply making sure not to touch the terminal between takes so they pieced together nicely. I didn&amp;rsquo;t fake anything here, although I certainly sped pieces up in post-processing.&lt;/p&gt;
&lt;p&gt;After I was done, I&amp;rsquo;d hand them off to ffmpeg to convert from 15fps to 24 so I didn&amp;rsquo;t need to deal with different framerates in Blender. I wish RecordMyDesktop would just do that for me.&lt;/p&gt;
&lt;h3 id=&#34;recording-audio&#34;&gt;Recording audio&lt;/h3&gt;
&lt;p&gt;I have a lot of experience with &lt;a href=&#34;http://www.audacityteam.org/&#34;&gt;Audacity&lt;/a&gt;, so that was my obvious choice. However, this time around (running v2.1.2 on Xenial) I ran into some stability issues. It crashed on me about once a session, and it didn&amp;rsquo;t always cut the track where I actually asked it to cut (sometimes I didn&amp;rsquo;t notice and ended up having to re-record a track). I still used it, and it did a good job of recovering when it crashed, but that was frustrating. Your mileage may vary (at least on that version). (&lt;strong&gt;Update:&lt;/strong&gt; On Bionic, Audacity no longer seems to be crashing, but it&amp;rsquo;ll still cut weird. I&amp;rsquo;ve started copying the original recordings into their own project before starting any editing.)&lt;/p&gt;
&lt;p&gt;I recorded each paragraph of the script as a separate track, edited them individually (e.g. removed the whirring sound of my server in the background), and then used &amp;ldquo;export multiple&amp;rdquo; to export them all at once so I could put them where they needed to go in the video editor.&lt;/p&gt;
&lt;h3 id=&#34;video-editor&#34;&gt;Video editor&lt;/h3&gt;
&lt;p&gt;Believe it or not, I use one of the prides of open-source: &lt;a href=&#34;https://www.blender.org/&#34;&gt;Blender&lt;/a&gt;. You&amp;rsquo;re thinking &amp;ldquo;Wait, isn&amp;rsquo;t Blender a 3D modeling program?&amp;rdquo; Yes, it is. But it also has the best video sequence editor (VSE) I&amp;rsquo;ve ever used. In fact, the only time I used its 3D capabilities was for the title floating in on &lt;a href=&#34;https://youtu.be/NUackegFqWQ?list=PL1LO5F1-Jh8K6R1Ba23irALtey6CRFFYu&#34;&gt;part 5&lt;/a&gt;. You probably didn&amp;rsquo;t notice. It took me four hours. Shut up.&lt;/p&gt;
&lt;p&gt;Blender is rock solid, but it has a lot of features and can be difficult to learn. Mike Meyers (mikeycal) created an &lt;a href=&#34;https://www.youtube.com/playlist?list=PLjyuVPBuorqIhlqZtoIvnAVQ3x18sNev4&#34;&gt;excellent series of tutorials&lt;/a&gt; for learning pretty much everything you need to know about the VSE, and I can&amp;rsquo;t recommend it enough.&lt;/p&gt;
&lt;p&gt;The only feature that&amp;rsquo;s missing at least in v2.76 that I&amp;rsquo;m using on Xenial is a simple way to do good-looking text. It has a text effect, but there&amp;rsquo;s no font control so it ends up just making me laugh. It sounds like they&amp;rsquo;re well aware of the limitation though&amp;ndash; I expect this is already fixed in later versions.&lt;/p&gt;
&lt;p&gt;As a work-around, I used &lt;a href=&#34;https://inkscape.org/en/&#34;&gt;Inkscape&lt;/a&gt; (and the wonderful Ubuntu font) to create the text you see in the videos. I made sure to place all the text in the same place (aligned left, vertically centered) and exported the entire page as a transparent PNG (so every text image was the same size). This allowed me to easily align the text in the editor and be confident they were all the same size.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll see that it took me a few videos to converge on a style: part 1 brought text into frame in a way that I felt was slightly gimmicky, so improving the text was a large focus in part 2. I liked that better, and you&amp;rsquo;ll see that I continued that style throughout the rest of the series.&lt;/p&gt;
&lt;p&gt;Editing took the majority of my time for each video, but it went faster as I gained experience. For example, editing was always the last step, but the order in which I actually did the editing morphed over time. For the first video, I pretty much perfected the video before adding the audio in. Then I realized that I had too much audio for the cuts I made, so I had to do all the cutting and transition work again. Like the efficient machine I am, I went through pretty much the same process for part 2 before I realized how much time I was wasting. Things went much smoother once I started importing all the raw footage and audio at the same time and aligning them before working on any cutting or transitions. Seems obvious in retrospect.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I had a good time with this project, and I see more videos in my future. I&amp;rsquo;ll probably slowly purchase better gear as time goes on, starting with a better slider and fluid head. But for the most part, this process worked well, and cost very little. I hope this post helps somebody out there!&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Call for testing: Darktable v2.2.4snap1</title>
      <link>https://kyrofa.com/posts/call-for-testing-darktable-v2-2-4snap1/</link>
      <pubDate>Thu, 18 May 2017 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/call-for-testing-darktable-v2-2-4snap1/</guid>
      <description>The Darktable snap (darktable-kyrofa) is terribly out-of-date. I&amp;rsquo;d like to bring it up to the latest release, but I could use your help verifying that things still work. If you&amp;rsquo;re a photographer, particularly if you have some experience using Darktable, mind putting it through its paces? v2.2.4snap1-rc1 is in the candidate channel:
$ sudo snap install --candidate darktable-kyrofa Please let me know if you run into any issues.
Update: v2.2.4snap1 has now been released into the stable channel.</description>
      <content:encoded>&lt;p&gt;The Darktable snap (&lt;code&gt;darktable-kyrofa&lt;/code&gt;) is terribly out-of-date. I&amp;rsquo;d like to bring it up to the latest release, but I could use your help verifying that things still work. If you&amp;rsquo;re a photographer, particularly if you have some experience using Darktable, mind putting it through its paces? &lt;code&gt;v2.2.4snap1-rc1&lt;/code&gt; is in the candidate channel:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install --candidate darktable-kyrofa
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Please let me know if you run into any issues.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; &lt;code&gt;v2.2.4snap1&lt;/code&gt; has now been released into the &lt;code&gt;stable&lt;/code&gt; channel.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Call for testing: Nextcloud snap v11.0.3snap2</title>
      <link>https://kyrofa.com/posts/call-for-testing-nextcloud-snap-v11-0-3snap2/</link>
      <pubDate>Thu, 11 May 2017 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/call-for-testing-nextcloud-snap-v11-0-3snap2/</guid>
      <description>We&amp;rsquo;re getting ready to release a new version of the Nextcloud snap with a few new features, including:
 Supporting changing ports from the default 80/443. Supporting Samba shares.  We&amp;rsquo;d love this new version (particularly these new features) to get some exercise before we release it. Feel like helping? We&amp;rsquo;ve put together some smoke tests to get you started.
Please test all you can, but if you only have time to focus on the smoke tests covering the new features, that would still be very useful.</description>
      <content:encoded>&lt;p&gt;We&amp;rsquo;re &lt;a href=&#34;https://forum.snapcraft.io/t/call-for-testing-nextcloud-snap-v11-0-3snap2/543&#34;&gt;getting ready to release&lt;/a&gt; a new version of the Nextcloud snap with a few new features, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Supporting changing ports from the default 80/443.&lt;/li&gt;
&lt;li&gt;Supporting Samba shares.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We&amp;rsquo;d love this new version (particularly these new features) to get some exercise before we release it. Feel like helping? We&amp;rsquo;ve put together some &lt;a href=&#34;https://github.com/nextcloud/nextcloud-snap/wiki/Smoke-Tests&#34;&gt;smoke tests&lt;/a&gt; to get you started.&lt;/p&gt;
&lt;p&gt;Please test all you can, but if you only have time to focus on the smoke tests covering the new features, that would still be very useful. Thank you!&lt;/p&gt;
&lt;p&gt;Let me know if you have any questions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Thanks to testers, we found issues with both major features. We&amp;rsquo;ve removed them from this release, which makes 11.0.3snap2 purely a security release. It has &lt;a href=&#34;https://github.com/nextcloud/nextcloud-snap/releases/tag/11.0.3snap2&#34;&gt;been released&lt;/a&gt;.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>ROS production: create an Ubuntu Core image with our snap preinstalled [5/5]</title>
      <link>https://kyrofa.com/posts/ros-production-create-an-ubuntu-core-image-with-our-snap-preinstalled-5-5/</link>
      <pubDate>Thu, 04 May 2017 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/ros-production-create-an-ubuntu-core-image-with-our-snap-preinstalled-5-5/</guid>
      <description>UPDATE: I&amp;rsquo;m leaving this series up for historical purposes, but please note that I no longer recommend Ubuntu Core or snaps for use in robotics.
This is the fifth (and final) blog post in this series about ROS production. In the previous post we created a gadget snap to allow confined access to the Turtlebot. In this post, we&amp;rsquo;re going to put all the pieces from this series together and create an Ubuntu Core image with our ROS snap preinstalled, ready to send to the factory for production.</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;UPDATE: I&amp;rsquo;m leaving this series up for historical purposes, but please note that I no longer recommend Ubuntu Core or snaps for use in robotics.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is the fifth (and final) blog post in &lt;a href=&#34;https://kyrofa.com/posts/from-ros-prototype-to-production-on-ubuntu-core-1-5&#34;&gt;this series about ROS production&lt;/a&gt;. In the &lt;a href=&#34;https://kyrofa.com/posts/ros-production-obtaining-confined-access-to-the-turtlebot-4-5&#34;&gt;previous post&lt;/a&gt; we created a &lt;code&gt;gadget&lt;/code&gt; snap to allow confined access to the Turtlebot. In this post, we&amp;rsquo;re going to put all the pieces from this series together and create an Ubuntu Core image with our ROS snap preinstalled, ready to send to the factory for production.&lt;/p&gt;
&lt;p&gt;If you download one of the generic Ubuntu Core images for a reference device and run &lt;code&gt;snap list&lt;/code&gt; on it immediately, you&amp;rsquo;ll see three snaps installed: a &lt;code&gt;core&lt;/code&gt; snap, a &lt;code&gt;kernel&lt;/code&gt; snap, and a &lt;code&gt;gadget&lt;/code&gt; snap.&lt;/p&gt;
&lt;p&gt;Every Ubuntu Core image consists of at least these three snaps. In my case, since I&amp;rsquo;m using an Intel NUC, I see &lt;code&gt;core&lt;/code&gt;, &lt;code&gt;pc-kernel&lt;/code&gt; and &lt;code&gt;pc-amd64-gadget&lt;/code&gt;. You may recognize that last one from &lt;a href=&#34;https://kyrofa.com/posts/ros-production-our-prototype-as-a-snap-3-5&#34;&gt;part 3&lt;/a&gt;, because it&amp;rsquo;s the gadget we started with when creating our custom one.&lt;/p&gt;
&lt;p&gt;We want to create an Ubuntu Core image that uses our custom gadget, and includes our ROS snap so that as soon as the device boots for the first time, the robot starts moving.&lt;/p&gt;
&lt;p&gt;Alright, let&amp;rsquo;s get started! Remember that this is also a video series: feel free to watch the video version of this post:&lt;/p&gt;

&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
  &lt;iframe src=&#34;https://www.youtube-nocookie.com/embed/NUackegFqWQ&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; allowfullscreen title=&#34;YouTube Video&#34;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h1 id=&#34;step-1-request-interface-auto-connection&#34;&gt;Step 1: Request interface auto-connection&lt;/h1&gt;
&lt;p&gt;As soon as the device boots for the first time, we want the Turtlebot to begin moving. In order for this to happen, the ROS snap needs access to the Turtlebot at boot. We added the correct slot to our gadget and plug to our app, but this interface &lt;em&gt;isn&amp;rsquo;t automatically connected&lt;/em&gt;, which means at boot, the ROS snap doesn&amp;rsquo;t actually have the appropriate permission to access the Turtlebot. This works okay for testing purposes, but to actually have a production image, we need this interface automatically connected. Eventually this will be automated, but currently it&amp;rsquo;s a manual process. You have to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Put the ROS snap in the store (which we did in &lt;a href=&#34;https://kyrofa.com/posts/ros-production-our-prototype-as-a-snap-3-5&#34;&gt;part 3&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Put the gadget snap in the store (which we did in &lt;a href=&#34;https://kyrofa.com/posts/ros-production-obtaining-confined-access-to-the-turtlebot-4-5&#34;&gt;part 4&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Create a new topic in the &lt;a href=&#34;https://forum.snapcraft.io/c/store&#34;&gt;Snapcraft forum&lt;/a&gt; requesting that the &lt;code&gt;kobuki&lt;/code&gt; interface (or whatever you called it) be automatically connected between these two snaps. This is done via an assertion on the gadget in the store.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; This is no longer the case. You can now specify auto-connection straight in the gadget instead of only relying on the store. As it should be.&lt;/p&gt;
&lt;h1 id=&#34;step-2-install-required-tools&#34;&gt;Step 2: Install required tools&lt;/h1&gt;
&lt;p&gt;Ubuntu Core will verify that the image it&amp;rsquo;s booting actually came from you. In order for that to happen, you need to create a signing key and register it with the store. You need Snapcraft and Snapd to generate, register, and use the signing key, and you need Ubuntu Image to actually create the image. You already have most of this from following the rest of the series, but for completeness:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt install snapcraft snapd ubuntu-image
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;step-3-create-and-register-your-signing-key&#34;&gt;Step 3: Create and register your signing key&lt;/h1&gt;
&lt;p&gt;Now let&amp;rsquo;s create a signing key:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft create-key my-key-name
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Check that everything worked and your key is ready:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft list-keys
  Name          SHA3-384 fingerprint
* my-key-name   Qjdfpj0EW&amp;lt;snip&amp;gt;iZ41H4CROy &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;not registered&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now register your new key with the store to associate it with your account:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft register-key
Registering key ...
Done. The key &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-key-name&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;Qjdfp&amp;lt;snip&amp;gt;H4CROy&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; may be used to sign your assertions.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;step-4-define-the-model&#34;&gt;Step 4: Define the model&lt;/h1&gt;
&lt;p&gt;In order to create an Ubuntu Core image, we need to create what&amp;rsquo;s called the &amp;ldquo;model definition.&amp;rdquo; The &amp;ldquo;model&amp;rdquo; in question is your device. The definition is simply a JSON document that answers the questions &amp;ldquo;what is this device, who created it, and what software is running on it?&amp;rdquo; The model definition and assertion I created are &lt;a href=&#34;https://github.com/kyrofa/turtlebot-model&#34;&gt;available for reference&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Create a file named &lt;code&gt;turtlebot-model.json&lt;/code&gt; and make it look something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;{
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;model&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;series&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;16&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;model&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;turtlebot-demo&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;architecture&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;amd64&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;kernel&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pc-kernel&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;gadget&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;my gadget snap&amp;gt;&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;authority-id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;my account ID&amp;gt;&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;brand-id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;my account ID&amp;gt;&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;timestamp&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;timestamp&amp;gt;&amp;#34;&lt;/span&gt;,
 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;required-snaps&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;my turtlebot snap&amp;gt;&amp;#34;&lt;/span&gt;]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We specify that this is targeting Ubuntu Core series 16, name the model &amp;ldquo;turtlebot-demo&amp;rdquo; (you can use whatever you wish), specify the architecture for this model as well as the kernel snap to use (these would change if your arch wasn&amp;rsquo;t amd64). Note that we&amp;rsquo;re using the kernel maintained and supported by Canonical here. The &lt;code&gt;gadget&lt;/code&gt; field needs to contain the name of your gadget snap. The &lt;code&gt;authority-id&lt;/code&gt; and &lt;code&gt;brand-id&lt;/code&gt; need to be the account ID from your store account. The timestamp needs to be the output of&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ date -Iseconds --utc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(I would explain why if I knew.) Finally, the &lt;code&gt;required-snaps&lt;/code&gt; is a list of snaps to be preinstalled on this image. In our case, that&amp;rsquo;s the name of the ROS snap we created in &lt;a href=&#34;https://kyrofa.com/posts/ros-production-our-prototype-as-a-snap-3-5&#34;&gt;part 3&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&#34;step-5-create-the-model-assertion&#34;&gt;Step 5: Create the model assertion&lt;/h1&gt;
&lt;p&gt;Now that we have a model definition, we need to use our key to sign it, thereby turning it into what&amp;rsquo;s called a &amp;ldquo;model assertion&amp;rdquo;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cat turtlebot-model.json | snap sign -k my-key-name &amp;gt; turtlebot.model
You need a passphrase to unlock the secret key &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt;
user: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-key-name&amp;#34;&lt;/span&gt;
4096-bit RSA key, ID 0B79B865, created 2016-01-01
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After giving your passphrase, a &lt;code&gt;turtlebot.model&lt;/code&gt; file is created containing the assertion.&lt;/p&gt;
&lt;h1 id=&#34;step-6-create-the-image&#34;&gt;Step 6: Create the image&lt;/h1&gt;
&lt;p&gt;The assertion we just created is all we need for &lt;code&gt;ubuntu-image&lt;/code&gt; to actually put the image together:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo ubuntu-image -c stable -o turtlebot.img turtlebot.model
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will pull down the various components of the image (&lt;code&gt;gadget&lt;/code&gt;, &lt;code&gt;kernel&lt;/code&gt;, &lt;code&gt;core&lt;/code&gt;, and any additional snaps from &lt;code&gt;required-snaps&lt;/code&gt;) from the &lt;code&gt;stable&lt;/code&gt; channel, and create an Ubuntu Core image out of them. Well, what if the &lt;code&gt;gadget&lt;/code&gt; (or other) snap is your own, and isn&amp;rsquo;t available in the store yet? The assertion doesn&amp;rsquo;t change, but you can provide local snaps to satisfy the snap names specified in the assertion with the &lt;code&gt;--extra-snaps&lt;/code&gt; parameter. For example, say your &lt;code&gt;gadget&lt;/code&gt; snap isn&amp;rsquo;t available in the store; you could use the local one with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo ubuntu-image -c stable -o turtlebot.img &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;                    --extra-snaps /path/to/gadget.snap &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;                    turtlebot.model
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We already mentioned the downsides to this, but it&amp;rsquo;s worth mentioning again: installing local snaps like this will prevent those snaps from updating, and will also prevent the &lt;code&gt;serial-port&lt;/code&gt; interface from auto-connecting.&lt;/p&gt;
&lt;p&gt;Once &lt;code&gt;ubuntu-image&lt;/code&gt; completes successfully, you&amp;rsquo;ll have an Ubuntu Core image ready to flash to your device!&lt;/p&gt;
&lt;h1 id=&#34;step-7-test-the-image&#34;&gt;Step 7: Test the image&lt;/h1&gt;
&lt;p&gt;You can take the image you just created for a test drive by &lt;a href=&#34;https://developer.ubuntu.com/core/get-started&#34;&gt;following the flashing directions&lt;/a&gt; for the device you used, or if you use amd64 you can even test it out in &lt;a href=&#34;https://kyrofa.com/posts/ubuntu-core-on-virtualbox&#34;&gt;VirtualBox&lt;/a&gt; or KVM.&lt;/p&gt;
&lt;p&gt;Once the image boots (assuming the Turtlebot is connected and powered on), the Turtlebot will begin moving. Congratulations, you&amp;rsquo;re done!&lt;/p&gt;
&lt;h2 id=&#34;step-7a-connect-interfaces-if-necessary&#34;&gt;Step 7a: Connect interfaces if necessary&lt;/h2&gt;
&lt;p&gt;If you were unable to follow &lt;a href=&#34;https://kyrofa.com/posts/ros-production-create-an-ubuntu-core-image-with-our-snap-preinstalled-5-5/#step-1-request-interface-auto-connection&#34;&gt;Step 1&lt;/a&gt;, then the Turtlebot probably didn&amp;rsquo;t begin moving for you once you powered the device on. This is due to the lack of interface auto-connection. To work around that, go through the first boot wizard as discussed on the flashing directions, SSH into the device, and run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snap interfaces
Slot                   Plug
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;...&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
my-gadget-snap:kobuki  -
-                      my-turtlebot-snap:kobuki
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, this interface is not connected. So connect them:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap connect my-turtlebot-snap:kobuki my-gadget-snap:kobuki
$ snap interfaces
Slot                   Plug
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;...&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
my-gadget-snap:kobuki  my-turtlebot-snap:kobuki
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The ROS snap now has permission to access the Turtlebot, but the service has probably errored out by now. So let&amp;rsquo;s start it again by disabling and re-enabling the snap:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap disable my-turtlebot-snap
$ sudo snap enable my-turtlebot-snap
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you should see the Turtlebot start moving as expected. This is persistent across reboots.&lt;/p&gt;
&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;At this point, we&amp;rsquo;ve taken a ROS prototype running on classic Ubuntu all the way to a production-ready Ubuntu Core image with the ROS prototype preinstalled and running on boot. Every component of this image will automatically update, including your ROS snap. Admittedly there are a few rough edges, but I hope this shows you how powerful snaps and Ubuntu Core can be.&lt;/p&gt;
&lt;p&gt;Hopefully you managed to follow along successfully, but if you ran into issues, here&amp;rsquo;s the reference material for this series:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/kyrofa/turtlebot-snap&#34;&gt;Turtlebot demo ROS snap&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Note that this is published as &lt;code&gt;turtlebot-demo-kyrofa&lt;/code&gt; in the store for all supported architectures (&lt;code&gt;stable&lt;/code&gt; channel)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/kyrofa/pc-amd64-turtlebot-gadget&#34;&gt;Turtlebot amd64 gadget&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Note that this is published as pc-turtlebot-kyrofa in the store (stable channel)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/kyrofa/dragonboard-turtlebot-gadget&#34;&gt;Turtlebot Dragonboard gadget&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Included for reference. Note that this is published as dragonboard-turtlebot-kyrofa in the store (stable channel)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/kyrofa/turtlebot-model&#34;&gt;Model definition and assertion used&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;This includes both amd64 and dragonboard model definitions/assertions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you need any help with this, or anything to do with ROS on snaps and/or Ubuntu Core, please feel free to reach out directly, ask in the &lt;a href=&#34;https://forum.snapcraft.io/&#34;&gt;Snapcraft forum&lt;/a&gt;, or ask in the &lt;a href=&#34;https://discourse.ros.org/&#34;&gt;ROS forum&lt;/a&gt;.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>ROS production: obtaining confined access to the Turtlebot [4/5]</title>
      <link>https://kyrofa.com/posts/ros-production-obtaining-confined-access-to-the-turtlebot-4-5/</link>
      <pubDate>Wed, 26 Apr 2017 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/ros-production-obtaining-confined-access-to-the-turtlebot-4-5/</guid>
      <description>UPDATE: I&amp;rsquo;m leaving this series up for historical purposes, but please note that I no longer recommend Ubuntu Core or snaps for use in robotics.
This is the fourth blog post in this series about ROS production. In the previous post we created a snap of our prototype, and released it into the store. In this post, we&amp;rsquo;re going to work toward an Ubuntu Core image by creating what&amp;rsquo;s called a gadget snap.</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;UPDATE: I&amp;rsquo;m leaving this series up for historical purposes, but please note that I no longer recommend Ubuntu Core or snaps for use in robotics.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is the fourth blog post in &lt;a href=&#34;https://kyrofa.com/posts/from-ros-prototype-to-production-on-ubuntu-core-1-5&#34;&gt;this series about ROS production&lt;/a&gt;. In the &lt;a href=&#34;https://kyrofa.com/posts/ros-production-our-prototype-as-a-snap-3-5&#34;&gt;previous post&lt;/a&gt; we created a snap of our prototype, and released it into the store. In this post, we&amp;rsquo;re going to work toward an Ubuntu Core image by creating what&amp;rsquo;s called a &lt;a href=&#34;https://github.com/snapcore/snapd/wiki/Gadget-snap&#34;&gt;&lt;code&gt;gadget&lt;/code&gt; snap&lt;/a&gt;. A &lt;code&gt;gadget&lt;/code&gt; snap contains information such as the bootloader bits, filesystem layout, etc., and is specific to the piece of hardware running Ubuntu Core. We don&amp;rsquo;t need anything special in that regard (read that link if you do), but the gadget snap is also currently the only place on Ubuntu Core and snaps in general where we can specify our udev symlink rule in order to obtain confined access to our Turtlebot (i.e. allow our snap to be installable and workable without &lt;code&gt;devmode&lt;/code&gt;). Eventually there will be a way to do this without requiring a custom gadget, but this is how it&amp;rsquo;s done today.&lt;/p&gt;
&lt;p&gt;Alright, let&amp;rsquo;s get started. Remember that this is also a video series&amp;ndash; feel free to watch the video version of this post:&lt;/p&gt;

&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
  &lt;iframe src=&#34;https://www.youtube-nocookie.com/embed/m9wqj34-2go&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; allowfullscreen title=&#34;YouTube Video&#34;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h1 id=&#34;step-1-start-with-an-existing-gadget&#34;&gt;Step 1: Start with an existing gadget&lt;/h1&gt;
&lt;p&gt;Canonical publishes gadget snaps for all reference devices, including generic amd64, i386, as well as the Raspberry Pi 2 and 3, and the DragonBoard 410c. If the computer on which you&amp;rsquo;re going to install Ubuntu Core is among these, you can start with a fork of the corresponding official gadget snap maintained in the &lt;a href=&#34;https://github.com/snapcore?utf8=%E2%9C%93&amp;amp;q=gadget&#34;&gt;github.com/snapcore&lt;/a&gt; organization. Since I&amp;rsquo;m using a NUC, I’ll start with a fork of &lt;a href=&#34;https://github.com/snapcore/pc-amd64-gadget&#34;&gt;pc-amd64-gadget&lt;/a&gt; (my finished gadget is &lt;a href=&#34;https://github.com/kyrofa/pc-amd64-turtlebot-gadget&#34;&gt;available for reference&lt;/a&gt;, and &lt;a href=&#34;https://github.com/kyrofa/dragonboard-turtlebot-gadget&#34;&gt;here&amp;rsquo;s one for the DragonBoard&lt;/a&gt;). If you&amp;rsquo;re using a different reference device, fork that gadget and you can still follow the rest of these steps.&lt;/p&gt;
&lt;h1 id=&#34;step-2-select-a-name-for-your-gadget-snap&#34;&gt;Step 2: Select a name for your gadget snap&lt;/h1&gt;
&lt;p&gt;Open the &lt;code&gt;snapcraft.yaml&lt;/code&gt; included in your gadget snap fork. You&amp;rsquo;ll see the same metadata we discussed in the previous post. Since snap names are globally unique, you need to decide on a different name for your gadget. For example, I selected pc-turtlebot-kyrofa. Once you settle on one, register it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft register my-gadget-snap-name
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If the registration succeeded, change the &lt;code&gt;snapcraft.yaml&lt;/code&gt; to reflect the new name. You can also update the summary, description, and version as necessary.&lt;/p&gt;
&lt;h1 id=&#34;step-3-add-the-required-udev-rule&#34;&gt;Step 3: Add the required udev rule&lt;/h1&gt;
&lt;p&gt;If you&amp;rsquo;ll recall, back in the &lt;a href=&#34;https://kyrofa.com/posts/ros-production-our-prototype-2-5&#34;&gt;second post in this series&lt;/a&gt;, we installed a udev rule to ensure that the Turtlebot showed up at &lt;code&gt;/dev/kobuki&lt;/code&gt;. Take a look at that rule:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cat /etc/udev/rules.d/57-kobuki.rules
&lt;span style=&#34;color:#75715e&#34;&gt;# On precise, for some reason, USER and GROUP are getting ignored.&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# So setting mode = 0666 for now.&lt;/span&gt;
SUBSYSTEM&lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tty&amp;#34;&lt;/span&gt;, ATTRS&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;idVendor&lt;span style=&#34;color:#f92672&#34;&gt;}==&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0403&amp;#34;&lt;/span&gt;, ATTRS&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;idProduct&lt;span style=&#34;color:#f92672&#34;&gt;}==&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;6001&amp;#34;&lt;/span&gt;, ATTRS&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;serial&lt;span style=&#34;color:#f92672&#34;&gt;}==&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kobuki*&amp;#34;&lt;/span&gt;, MODE:&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0666&amp;#34;&lt;/span&gt;, GROUP:&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dialout&amp;#34;&lt;/span&gt;, SYMLINK&lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kobuki&amp;#34;&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# Bluetooth module (currently not supported and may have problems)&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# SUBSYSTEM==&amp;#34;tty&amp;#34;, ATTRS{address}==&amp;#34;00:00:00:41:48:22&amp;#34;, MODE:=&amp;#34;0666&amp;#34;, GROUP:=&amp;#34;dialout&amp;#34;, SYMLINK+=&amp;#34;kobuki&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s outside the scope of this series to explain udev in depth, but the two values I&amp;rsquo;ve made bold are how the Kobuki base is uniquely identified. We&amp;rsquo;re going to write the snapd version of this rule, using the same values. At the end of the gadget&amp;rsquo;s &lt;code&gt;snapcraft.yaml&lt;/code&gt;, add the following slot definition:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Custom serial-port interface for the Turtlebot 2&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;slots&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;kobuki&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;interface&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;serial-port&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;path&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;/dev/serial-port-kobuki&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;usb-vendor&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0x0403&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;usb-product&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0x6001&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The name of the slot being defined is &lt;code&gt;kobuki&lt;/code&gt;. The symlink path we&amp;rsquo;re requesting is &lt;code&gt;/dev/serial-port-kobuki&lt;/code&gt;. Why not &lt;code&gt;/dev/kobuki&lt;/code&gt;? Because snapd only supports namespaced serial port symlinks to avoid conflicts, without documentation, and with silent errors (it&amp;rsquo;s lovely, I&amp;rsquo;m not grumpy about it). You can use whatever you like for this path (and the slot name), but make sure to follow the &lt;code&gt;/dev/serial-port-&amp;lt;whatever&amp;gt;&lt;/code&gt; pattern, and adjust the rest of the directions in this series accordingly.&lt;/p&gt;
&lt;h1 id=&#34;step-4-build-the-gadget-snap&#34;&gt;Step 4: Build the gadget snap&lt;/h1&gt;
&lt;p&gt;This step is easy. Just get into the gadget snap directory and run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the end, you&amp;rsquo;ll have a gadget snap.&lt;/p&gt;
&lt;h1 id=&#34;step-5-put-the-gadget-snap-in-the-store&#34;&gt;Step 5: Put the gadget snap in the store&lt;/h1&gt;
&lt;p&gt;You don&amp;rsquo;t technically need the gadget snap in the store just to create an image, but there are two reasons you &lt;em&gt;will&lt;/em&gt; want it there:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Without putting it in the store you have no way of updating it in the future&lt;/li&gt;
&lt;li&gt;Without putting it in the store it&amp;rsquo;s impossible for the serial-port interface we just added to be automatically connected to the snap that needs it, which means the image won&amp;rsquo;t make the robot move out of the box (&lt;strong&gt;Update:&lt;/strong&gt; This is no longer true, you can specify auto-connections within the gadget now)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Since you&amp;rsquo;ve already registered the snap name, just push it up (we want our image based on the &lt;code&gt;stable&lt;/code&gt; channel, so let&amp;rsquo;s release into that):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft push /path/to/my/gadget.snap --release&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;stable
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You will in all likelihood receive an error saying it&amp;rsquo;s been queued for manual review since it&amp;rsquo;s a gadget snap. It&amp;rsquo;s true, right now gadget snaps require manual review (although that will change soon). You&amp;rsquo;ll need to wait for this review to complete successfully before you can take advantage of it actually being in the store (make a post in the &lt;a href=&#34;https://forum.snapcraft.io/c/store&#34;&gt;store category of the forum&lt;/a&gt; if it takes longer than you expect), but you can continue following this series while you wait. I&amp;rsquo;ll highlight things you&amp;rsquo;ll need to do differently if your gadget snap isn&amp;rsquo;t yet available in the store.&lt;/p&gt;
&lt;h1 id=&#34;step-6-adjust-our-ros-snap-to-run-under-strict-confinement&#34;&gt;Step 6: Adjust our ROS snap to run under strict confinement&lt;/h1&gt;
&lt;p&gt;As you&amp;rsquo;ll remember, our ROS snap currently only runs with &lt;code&gt;devmode&lt;/code&gt;, and still assumes the Turtlebot is available at &lt;code&gt;/dev/kobuki&lt;/code&gt;. We know now that our gadget snap will make the Turtlebot available via a slot at &lt;code&gt;/dev/serial-port-kobuki&lt;/code&gt;, so we need to alter our snap slightly to account for this. Fortunately, when we initially created the prototype, we made the serial port configurable. Let&amp;rsquo;s pat ourselves on the back for a moment, then edit our snapcraft.yaml a bit:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;my-turtlebot-snap&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0.2&amp;#39;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;summary&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Turtlebot ROS Demo&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;description&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; &lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;Demo of Turtlebot randomly wandering around, avoiding obstacles and cliffs.&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;grade&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;stable&lt;/span&gt;

&lt;span style=&#34;color:#75715e&#34;&gt;# Using strict confinement here, but that only works if installed&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# on an image that exposes /dev/serial-port-kobuki via the gadget.&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;confinement&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;strict&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;parts&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;prototype-workspace&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;catkin&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;rosdistro&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;kinetic&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;catkin-packages&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;prototype]&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;plugs&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;kobuki&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;interface&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;serial-port&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;apps&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;system&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;command&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;roslaunch prototype prototype.launch device_port:=/dev/serial-port-kobuki --screen&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;plugs&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;network, network-bind, kobuki]&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;daemon&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;simple&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Most of this is unchanged from version 0.1 of our snap that we discussed in the previous post. Let’s cover only the parts that changed.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0.2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;re modifying the snap here, so I suggest changing the version. This isn&amp;rsquo;t required (the &lt;code&gt;version&lt;/code&gt; field is only for human consumption, it&amp;rsquo;s not used to determine which snap is newest), but it certainly makes support easier (&amp;ldquo;what version of the snap are you on?&amp;quot;).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Using strict confinement here, but that only works if installed&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# on an image that exposes /dev/serial-port-kobuki via the gadget.&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;confinement&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;strict&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You wrote such a nice comment, it hardly needs more explanation. The point is, now that we have a gadget that makes this device accessible, we can switch to using strict confinement.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;plugs&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;kobuki&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;interface&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;serial-port&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This one defines a new plug in this snap, but then says &amp;ldquo;the kobuki plug is just a serial-port plug.&amp;rdquo; This is optional, but it&amp;rsquo;s handy to have our plug named &lt;code&gt;kobuki&lt;/code&gt; instead of the more generic &lt;code&gt;serial-port&lt;/code&gt;. If you opt to leave this off, keep it in mind for the following section.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apps&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;system&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;command&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;roslaunch prototype prototype.launch device_port:=/dev/serial-port-kobuki --screen&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;plugs&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;network, network-bind, kobuki]&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;daemon&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;simple&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we take advantage of the flexibility we introduced in the &lt;a href=&#34;https://kyrofa.com/posts/ros-production-our-prototype-2-5&#34;&gt;second post of the series&lt;/a&gt;, and specify that our launch file should be launched with &lt;code&gt;device_port&lt;/code&gt; set to the udev symlink defined by the gadget instead of the default &lt;code&gt;/dev/kobuki&lt;/code&gt;. We also specify that this app should utilize the &lt;code&gt;kobuki&lt;/code&gt; plug defined directly above it, which grants it confined access to the serial port.&lt;/p&gt;
&lt;p&gt;Rebuild your snap, and release the updated version into the store as we covered in &lt;a href=&#34;https://kyrofa.com/posts/ros-production-our-prototype-as-a-snap-3-5&#34;&gt;part 3&lt;/a&gt;, but now you can release to the &lt;code&gt;stable&lt;/code&gt; channel. Note that this isn&amp;rsquo;t required, but the reasons for putting this snap in the store are the same as the reasons for putting the gadget in the store (namely, updatability and interface auto-connection).&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&#34;https://kyrofa.com/posts/ros-production-create-an-ubuntu-core-image-with-our-snap-preinstalled-5-5&#34;&gt;next (and final) post in this series&lt;/a&gt;, we&amp;rsquo;ll put all these pieces together and create an Ubuntu Core image that is ready for the factory.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>ROS production: our prototype as a snap [3/5]</title>
      <link>https://kyrofa.com/posts/ros-production-our-prototype-as-a-snap-3-5/</link>
      <pubDate>Wed, 19 Apr 2017 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/ros-production-our-prototype-as-a-snap-3-5/</guid>
      <description>UPDATE: I&amp;rsquo;m leaving this series up for historical purposes, but please note that I no longer recommend Ubuntu Core or snaps for use in robotics.
This is the third blog post in this series about ROS production. In the previous post we came up with a simple ROS prototype. In this post we&amp;rsquo;ll package that prototype as a snap. For justifications behind why we&amp;rsquo;re doing this, please see the first post in the series.</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;UPDATE: I&amp;rsquo;m leaving this series up for historical purposes, but please note that I no longer recommend Ubuntu Core or snaps for use in robotics.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is the third blog post in &lt;a href=&#34;https://kyrofa.com/posts/from-ros-prototype-to-production-on-ubuntu-core-1-5&#34;&gt;this series about ROS production&lt;/a&gt;. In the &lt;a href=&#34;https://kyrofa.com/posts/ros-production-our-prototype-2-5&#34;&gt;previous post&lt;/a&gt; we came up with a simple ROS prototype. In this post we&amp;rsquo;ll package that prototype as a snap. For justifications behind why we&amp;rsquo;re doing this, please see the &lt;a href=&#34;https://kyrofa.com/posts/from-ros-prototype-to-production-on-ubuntu-core-1-5&#34;&gt;first post in the series&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We know from the previous post that our prototype consists of a single launch file that we wrote, contained within our &lt;code&gt;prototype&lt;/code&gt; ROS package. Turning this into a snap is very straight-forward, so let&amp;rsquo;s get started! Remember that this is also a video series: feel free to watch the video version of this post:&lt;/p&gt;

&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
  &lt;iframe src=&#34;https://www.youtube-nocookie.com/embed/Dw1aqbucPbY&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; allowfullscreen title=&#34;YouTube Video&#34;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h1 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h1&gt;
&lt;p&gt;This post will assume the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You&amp;rsquo;ve followed the previous posts in this series&lt;/li&gt;
&lt;li&gt;You know what snaps are, and have taken the tour&lt;/li&gt;
&lt;li&gt;You have a store account at dashboard.snapcraft.io&lt;/li&gt;
&lt;li&gt;You have a recent Snapcraft installed (2.28 is the latest as of this writing)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;create-the-snap&#34;&gt;Create the snap&lt;/h1&gt;
&lt;p&gt;The first step toward a new snap is to create the &lt;code&gt;snapcraft.yaml&lt;/code&gt;. Put that in the root of the workspace we created in the previous post:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/workspace
$ snapcraft init
Created snap/snapcraft.yaml.
Edit the file to your liking or run &lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;snapcraft&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt; to get started
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Do as it says, and make that file look something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;my-turtlebot-snap&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# This needs to be a unique name&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0.1&amp;#39;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;summary&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Turtlebot ROS Demo&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;description&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; &lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;Demo of Turtlebot randomly wandering around, avoiding obstacles and cliffs.&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;grade&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;stable&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;confinement&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;devmode&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;parts&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;prototype-workspace&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;catkin&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;rosdistro&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;kinetic&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;catkin-packages&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;prototype]&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;apps&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;system&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;command&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;roslaunch prototype prototype.launch --screen&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;plugs&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;network, network-bind]&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;daemon&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;simple&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s digest that section by section.&lt;/p&gt;
&lt;p&gt;name: my-turtlebot-snap
version: 0.1
summary: Turtlebot ROS Demo
description: |
Demo of Turtlebot randomly wandering around, avoiding obstacles and cliffs.&lt;/p&gt;
&lt;p&gt;This is the basic metadata that all snaps require. These fields are fairly self-explanatory. The only thing I want to point out specifically here is that the &lt;code&gt;name&lt;/code&gt; must be globally unique among all snaps. If you&amp;rsquo;re following this tutorial, you might consider appending your developer name to the end of this example.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;grade&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;stable&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;confinement&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;devmode&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;grade&lt;/code&gt; can be either &lt;code&gt;stable&lt;/code&gt; or &lt;code&gt;devel&lt;/code&gt;. If it&amp;rsquo;s &lt;code&gt;devel&lt;/code&gt;, the store will prevent you from releasing into one of the two stable channels (&lt;code&gt;stable&lt;/code&gt; and &lt;code&gt;candidate&lt;/code&gt;, specifically)&amp;ndash; think of it as a safety net to prevent accidental releases. If it&amp;rsquo;s stable, you can release it anywhere.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;confinement&lt;/code&gt; can be &lt;code&gt;strict&lt;/code&gt;, &lt;code&gt;devmode&lt;/code&gt;, or &lt;code&gt;classic&lt;/code&gt;. &lt;code&gt;strict&lt;/code&gt; enforces confinement, whereas &lt;code&gt;devmode&lt;/code&gt; allows all accesses, even those that would be disallowed under &lt;code&gt;strict&lt;/code&gt; confinement (and logs accesses that would otherwise be disallowed for your reference). &lt;code&gt;classic&lt;/code&gt; is even less confined than &lt;code&gt;devmode&lt;/code&gt;, in that it doesn&amp;rsquo;t even get private namespaces anymore (among other things). There is &lt;a href=&#34;https://snapcraft.io/docs/reference/confinement&#34;&gt;more extensive documentation on confinement available&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I personally always use &lt;code&gt;strict&lt;/code&gt; confinement unless I know for sure that the thing I&amp;rsquo;m snapping won&amp;rsquo;t run successfully under confinement, in which case I&amp;rsquo;ll use &lt;code&gt;devmode&lt;/code&gt;. I typically avoid &lt;code&gt;classic&lt;/code&gt; unless I never intend for the app to run confined. Also, &lt;code&gt;classic&lt;/code&gt; confinement requires manual review in the store, a process that generally isn&amp;rsquo;t that much fun.&lt;/p&gt;
&lt;p&gt;In this case, I know from experience this snap won&amp;rsquo;t run confined as-is, and will require &lt;code&gt;devmode&lt;/code&gt; for now (more on that later).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;parts&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;prototype-workspace&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;catkin&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;rosdistro&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;kinetic&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;catkin-packages&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;prototype]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You learned about this in the Snapcraft tour, but I&amp;rsquo;ll cover it again real quick. Snapcraft is responsible for taking many disparate parts and orchestrating them all into one cohesive snap. You tell it the parts that make up your snap, and it takes care of the rest. Here, we tell Snapcraft that we have a single part called &lt;code&gt;prototype-workspace&lt;/code&gt;. We specify that it builds with Catkin, and also specify that we&amp;rsquo;re using Kinetic here (as opposed to Jade, or the default, Indigo). Finally, we specify the packages in this workspace that we want included in the snap. In our case, we only have one: that &lt;code&gt;prototype&lt;/code&gt; package we created in part 2.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apps&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;system&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;command&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;roslaunch prototype prototype.launch --screen&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;plugs&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;network, network-bind]&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;daemon&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;simple&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is where things get a little interesting. When we build this snap, it will include a complete ROS system: roscpp, roslib, roscore, roslaunch, your ROS workspace, etc. It&amp;rsquo;s a standalone unit: you&amp;rsquo;re in total control of how the user interacts with it. You exercise that control via the &lt;code&gt;apps&lt;/code&gt; keyword, where you expose specific commands to the user. Here, we specify that this snap has a single app, called &lt;code&gt;system&lt;/code&gt;. The command that this app actually runs within the snap is the &lt;code&gt;roslaunch&lt;/code&gt; invocation we got from part 2. We use &lt;code&gt;plugs&lt;/code&gt; to specify that it requires network access (&lt;a href=&#34;https://snapcraft.io/docs/core/interfaces&#34;&gt;read more about interfaces&lt;/a&gt;), and finally specify that it&amp;rsquo;s a simple daemon. That means this app will begin running as soon as the snap is installed, and also run upon boot. All this, and the user doesn&amp;rsquo;t even need to know that this snap uses ROS!&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s actually all we need to make our prototype into a snap. Let&amp;rsquo;s create the snap itself:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/workspace
$ snapcraft
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That will take a few minutes. You&amp;rsquo;ll see Snapcraft fetch &lt;code&gt;rosdep&lt;/code&gt;, which is then used to determine the dependencies of the ROS packages in the workspace. This is only &lt;code&gt;prototype&lt;/code&gt; in our case, which you&amp;rsquo;ll recall from the previous post depends upon &lt;code&gt;kobuki_node&lt;/code&gt; and &lt;code&gt;kobuki_random_walker&lt;/code&gt;. It then pulls those down and puts them into the snap along with &lt;code&gt;roscore&lt;/code&gt;. Finally, it builds the requested packages in the workspace, and installs them into the snap as well. At the end, you&amp;rsquo;ll have your snap.&lt;/p&gt;
&lt;h1 id=&#34;test-the-snap&#34;&gt;Test the snap&lt;/h1&gt;
&lt;p&gt;Even though we&amp;rsquo;re planning on using this snap on Ubuntu Core, snaps run on classic Ubuntu as well. This is an excellent way to ensure that our snap runs as expected before moving on to Ubuntu Core. Since we already have our machine setup to communicate with the Turtlebot, we can try it out right here. The only hitch is that &lt;code&gt;/dev/kobuki&lt;/code&gt; isn&amp;rsquo;t covered by any interface on classic Ubuntu (we can make this work for Ubuntu Core, though, more on that later). That&amp;rsquo;s why we used &lt;code&gt;devmode&lt;/code&gt; as the confinement type in our snap. We&amp;rsquo;ll install it with &lt;code&gt;devmode&lt;/code&gt; here:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install --devmode path/to/my.snap
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Right after this completes (give it a second for our &lt;code&gt;app &lt;/code&gt;to fire up), you should hear the robot sing and begin moving. Once you remove the snap it&amp;rsquo;ll stop moving:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap remove my-turtlebot-snap
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;How easy is that? If you put that in the store, anyone with a Turtlebot (no ROS required) could snap install it and it would immediately begin moving just like it did for you. In fact, why don&amp;rsquo;t we put it in the store right now?&lt;/p&gt;
&lt;h1 id=&#34;put-the-snap-in-the-store&#34;&gt;Put the snap in the store&lt;/h1&gt;
&lt;h2 id=&#34;step-1-tell-snapcraft-who-you-are&#34;&gt;Step 1: Tell Snapcraft who you are&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;re about to use Snapcraft to register and upload a snap using the store account you created when satisfying the prerequisites. For that to happen, you need to sign in with Snapcraft:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft login
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;step-2-register-the-snap-name&#34;&gt;Step 2: Register the snap name&lt;/h2&gt;
&lt;p&gt;Snap names are globally unique, so only one developer can register and publish a snap with a given name. Before you can publish the snap, you need to make sure that snap name is registered to you (note that this corresponds to the &lt;code&gt;name&lt;/code&gt; field in the &lt;code&gt;snapcraft.yaml&lt;/code&gt; we created a few minutes ago):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft register &amp;lt;my snap name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Assuming that name is available, you can proceed to upload it.&lt;/p&gt;
&lt;h2 id=&#34;step-3-release-the-snap&#34;&gt;Step 3: Release the snap&lt;/h2&gt;
&lt;p&gt;In the tour you learned that there are four channels available by default. In order of increasing stability, these channels are edge, beta, candidate, and stable. This snap isn&amp;rsquo;t quite perfect yet since it still requires &lt;code&gt;devmode&lt;/code&gt;, so let&amp;rsquo;s release it on the beta channel:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft push path/to/my.snap --release&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;beta
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once the upload and automated reviews finish successfully, anyone in the world can install your snap on the computer controlling their Turtlebot as simply as:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install --beta --devmode my-turtlebot-snap
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the &lt;a href=&#34;https://kyrofa.com/posts/ros-production-obtaining-confined-access-to-the-turtlebot-4-5&#34;&gt;next post in this series&lt;/a&gt;, we&amp;rsquo;ll discuss how to obtain real confined access to the Turtlebot&amp;rsquo;s udev symlink on Ubuntu Core by creating a &lt;code&gt;gadget&lt;/code&gt; snap, moving toward our goal of having a final image with this snap pre-installed and ready to ship.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>ROS production: our prototype [2/5]</title>
      <link>https://kyrofa.com/posts/ros-production-our-prototype-2-5/</link>
      <pubDate>Wed, 12 Apr 2017 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/ros-production-our-prototype-2-5/</guid>
      <description>UPDATE: I&amp;rsquo;m leaving this series up for historical purposes, but please note that I no longer recommend Ubuntu Core or snaps for use in robotics.
This is the second blog post in this series about ROS production. In the previous post we discussed why Ubuntu Core was a good fit for production robotics. In this post we&amp;rsquo;ll be on classic Ubuntu, creating the example ROS prototype that we&amp;rsquo;ll use throughout the rest of the series as we work toward using Ubuntu Core.</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;UPDATE: I&amp;rsquo;m leaving this series up for historical purposes, but please note that I no longer recommend Ubuntu Core or snaps for use in robotics.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is the second blog post in &lt;a href=&#34;https://kyrofa.com/posts/from-ros-prototype-to-production-on-ubuntu-core-1-5&#34;&gt;this series about ROS production&lt;/a&gt;. In the &lt;a href=&#34;https://kyrofa.com/posts/from-ros-prototype-to-production-on-ubuntu-core-1-5&#34;&gt;previous post&lt;/a&gt; we discussed why Ubuntu Core was a good fit for production robotics. In this post we&amp;rsquo;ll be on classic Ubuntu, creating the example ROS prototype that we&amp;rsquo;ll use throughout the rest of the series as we work toward using Ubuntu Core.&lt;/p&gt;
&lt;p&gt;I mentioned in the previous post that this series was going to use the &lt;a href=&#34;http://www.turtlebot.com/&#34;&gt;Turtlebot 2&lt;/a&gt;. However, there are a number of possible configurations for the Turtlebot. Some use a Kinect, others use an Astra Pro, still others use a RealSense, etc. I want this prototype to be runnable by everyone who has a Turtlebot 2, so I&amp;rsquo;m going to cater to the lowest common denominator here: the Kobuki base. We&amp;rsquo;ll keep it very simple and make it randomly wander around, bumping into things and correcting itself, utilizing both the bumper sensors as well as the cliff sensors.&lt;/p&gt;
&lt;p&gt;I also mentioned how the ROS community has standardized around the Turtlebot as the introductory platform. That makes this prototype incredibly easy to create, as every package we need has already been written: we just need to put them together. Let&amp;rsquo;s get started! Remember that this is also a video series: feel free to watch the video version of this post:&lt;/p&gt;

&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
  &lt;iframe src=&#34;https://www.youtube-nocookie.com/embed/QFrOXd2avfo&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; allowfullscreen title=&#34;YouTube Video&#34;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;This post will assume the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You’re running on Ubuntu 16.04 (Xenial)&lt;/li&gt;
&lt;li&gt;You have &lt;a href=&#34;http://wiki.ros.org/kinetic/Installation/Ubuntu&#34;&gt;ROS Kinetic installed&lt;/a&gt; (I installed &lt;code&gt;ros-kinetic-ros-base&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;You have gone through at least the first 8 &lt;a href=&#34;http://wiki.ros.org/ROS/Tutorials&#34;&gt;ROS tutorials&lt;/a&gt; (e.g. you should know what a launch file is)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The computer being used doesn&amp;rsquo;t matter so much as this prototype should work on any standard architecture, but for your reference I&amp;rsquo;m using an Intel NUC (the &lt;a href=&#34;https://ark.intel.com/products/78577/Intel-NUC-Kit-DE3815TYKHE&#34;&gt;DE3815TYKE&lt;/a&gt;, specifically).&lt;/p&gt;
&lt;h2 id=&#34;the-prototype&#34;&gt;The prototype&lt;/h2&gt;
&lt;h3 id=&#34;step-1-install-the-kobuki-driver&#34;&gt;Step 1: Install the Kobuki driver&lt;/h3&gt;
&lt;p&gt;The first Turtlebot-specific package we&amp;rsquo;ll need is the &lt;a href=&#34;http://wiki.ros.org/kobuki_node&#34;&gt;ROS wrapper for the Kobuki driver&lt;/a&gt;. It contains the node that&amp;rsquo;s responsible for communicating with the Kobuki itself, receiving sensor data and publishing it on ROS, etc.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt install ros-kinetic-kobuki-node
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;step-2-configure-udev-rule&#34;&gt;Step 2: Configure udev rule&lt;/h3&gt;
&lt;p&gt;By default, the driver will look for the Turtlebot at &lt;code&gt;/dev/kobuki&lt;/code&gt;. Getting the Kobuki there takes a udev rule. Thankfully, what we just installed includes a utility for installing this udev rule:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ rosrun kobuki_ftdi create_udev_rules
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It may prompt for your &lt;code&gt;sudo&lt;/code&gt; password. After this completes successfully, connect your Turtlebot, turn it on, and you should see the &lt;code&gt;/dev/kobuki&lt;/code&gt; symlink show up.&lt;/p&gt;
&lt;h3 id=&#34;step-3-test-the-kobuki-driver&#34;&gt;Step 3: Test the Kobuki driver&lt;/h3&gt;
&lt;p&gt;We can prove that this is working by running the driver&amp;rsquo;s &lt;code&gt;minimal&lt;/code&gt; launch file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ roslaunch kobuki_node minimal.launch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After the system comes up, you should hear the Turtlebot sing a little tune. If that doesn’t happen, check to make sure that the udev rule is working. If it does happen, leave it running and continue.&lt;/p&gt;
&lt;h3 id=&#34;step-4-install-the-kobuki-random-walker&#34;&gt;Step 4: Install the Kobuki Random Walker&lt;/h3&gt;
&lt;p&gt;The Kobuki driver is most of what we need. Its minimal launch file brings up enough nodes to obtain data from the wheel drop sensors, cliff sensors, etc., but most importantly exposes the ability to command the robot to move. We just need to make use of it. We want it to randomly walk around, so we&amp;rsquo;ll use the &lt;a href=&#34;http://wiki.ros.org/kobuki_random_walker&#34;&gt;Kobuki Random Walker&lt;/a&gt; (note that this may already be installed):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt install ros-kinetic-kobuki-random-walker
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;step-5-test-the-kobuki-random-walker&#34;&gt;Step 5: Test the Kobuki Random Walker&lt;/h3&gt;
&lt;p&gt;To run it (the &lt;code&gt;minimal.launch&lt;/code&gt; file from &lt;code&gt;kobuki_node&lt;/code&gt; still needs to be running):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ roslaunch kobuki_random_walker safe_random_walker_app.launch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When that system finishes launching, the robot will begin randomly wandering around. If it collides with something using its bumper sensors, it&amp;rsquo;ll back up and rotate, trying to get around it. If it notices it&amp;rsquo;s about to drive off a cliff (e.g. your steps) it’ll stop and rotate again.&lt;/p&gt;
&lt;p&gt;Go ahead and stop (ctrl+c) both launch files.&lt;/p&gt;
&lt;h3 id=&#34;step-6-put-the-pieces-together&#34;&gt;Step 6: Put the pieces together&lt;/h3&gt;
&lt;p&gt;At this point, if you have the appropriate 3d sensors, you could start doing the really fun stuff with the navigation stack, but as I mentioned before, we&amp;rsquo;re going to keep it simple here. These two launch files make up the entirety of the prototype we&amp;rsquo;ll use for this series. However, if we leave it as-is it has a small limitation: the only udev symlink location supported is &lt;code&gt;/dev/kobuki&lt;/code&gt;. That works on our current system, but what if another user has it elsewhere? Or what if we couldn&amp;rsquo;t use that exact symlink in a snap (a hint for the next post in this series)? So let&amp;rsquo;s distill those two launch files into a single launch file that represents our prototype, and make it a little more configurable.&lt;/p&gt;
&lt;p&gt;First of all, create a new ROS workspace for this prototype (you can of course put this anywhere):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ mkdir -p ~/workspace/src
$ cd ~/workspace/src
$ catkin_init_workspace
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now create a new package within that workspace to contain our prototype&amp;rsquo;s launch file. It should depend upon the two packages we know we need (&lt;code&gt;kobuki_node&lt;/code&gt; and &lt;code&gt;kobuki_random_walker&lt;/code&gt;):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/workspace/src
$ catkin_create_pkg prototype kobuki_node kobuki_random_walker
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we&amp;rsquo;re going to create our launch file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/workspace/src/prototype
$ mkdir launch
$ touch launch/prototype.launch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Open that &lt;code&gt;launch/prototype.launch&lt;/code&gt; file in your favorite editor. In your terminal, open up the &lt;code&gt;minimal.launch&lt;/code&gt; file from the Kobuki driver:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ rosed kobuki_node minimal.launch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;ll copy the contents of that launch file into ours, clean it up into only the bits we need, and add an argument that makes the serial port configurable. Finally, we&amp;rsquo;re also going to include the random walker launch file so that launching this single launch file brings up our entire prototype:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;launch&amp;gt;&lt;/span&gt;
   &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- Make the device port configurable. --&amp;gt;&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;arg&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;device_port&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;default=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/dev/kobuki&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;

   &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!--
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;       Nodelet manager for the Kobuki driver as well as the safe
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;       walker.
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;   --&amp;gt;&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;node&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;pkg=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;nodelet&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;type=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;nodelet&amp;#34;&lt;/span&gt;
         &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mobile_base_nodelet_manager&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;args=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;manager&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;

   &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- The ROS wrapper for the Kobuki driver. --&amp;gt;&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;node&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;pkg=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;nodelet&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;type=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;nodelet&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mobile_base&amp;#34;&lt;/span&gt;
         &lt;span style=&#34;color:#a6e22e&#34;&gt;args=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;load kobuki_node/KobukiNodelet mobile_base_nodelet_manager&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
       &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;rosparam&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;file=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$(find kobuki_node)/param/base.yaml&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;command=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;load&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
       &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;param&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;device_port&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$(arg device_port)&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
       &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;remap&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;from=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mobile_base/odom&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;to=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;odom&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
       &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;remap&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;from=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mobile_base/joint_states&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;to=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;joint_states&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/node&amp;gt;&lt;/span&gt;

   &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!--
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;       The safe random walker launch file that causes the robot
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;       to wander while trying not to destroy itself by
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;       utilizing the bumper sensors as well as the cliff
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;       sensors.
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;   --&amp;gt;&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;include&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;file=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$(find kobuki_random_walker)/launch/safe_random_walker_app.launch&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/launch&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Save and exit. We also need to modify &lt;code&gt;~/workspace/src/prototype/CMakeLists.txt&lt;/code&gt; to actually install that launch file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-cmake&#34; data-lang=&#34;cmake&#34;&gt;cmake_minimum_required(&lt;span style=&#34;color:#e6db74&#34;&gt;VERSION&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;2.8.3&lt;/span&gt;)&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;project(&lt;span style=&#34;color:#e6db74&#34;&gt;prototype&lt;/span&gt;)&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;find_package(&lt;span style=&#34;color:#e6db74&#34;&gt;catkin&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;REQUIRED&lt;/span&gt;)&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;catkin_package()&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;install(&lt;span style=&#34;color:#e6db74&#34;&gt;DIRECTORY&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;launch&lt;/span&gt;
   &lt;span style=&#34;color:#e6db74&#34;&gt;DESTINATION&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;${&lt;/span&gt;CATKIN_PACKAGE_SHARE_DESTINATION&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
)&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Save and exit. Note that the entire ROS project used here is &lt;a href=&#34;https://github.com/kyrofa/turtlebot-snap/tree/master/src/prototype&#34;&gt;available for reference&lt;/a&gt;. Finally, build the workspace:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/workspace
$ catkin_make
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now test out our final prototype:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ source ~/workspace/devel/setup.sh
$ roslaunch prototype prototype.launch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the robot should sing its little tune and immediately begin moving randomly again. So now we have our prototype contained in a single launch file, and we can use a different serial port like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ roslaunch prototype prototype.launch device_port:&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/dev/my-serial-port
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the &lt;a href=&#34;https://kyrofa.com/posts/ros-production-our-prototype-as-a-snap-3-5&#34;&gt;next post in this series&lt;/a&gt;, we&amp;rsquo;ll discuss the process of packaging this prototype as a snap.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>From ROS prototype to production on Ubuntu Core [1/5]</title>
      <link>https://kyrofa.com/posts/from-ros-prototype-to-production-on-ubuntu-core-1-5/</link>
      <pubDate>Wed, 05 Apr 2017 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/from-ros-prototype-to-production-on-ubuntu-core-1-5/</guid>
      <description>UPDATE: I&amp;rsquo;m leaving this series up for historical purposes, but please note that I no longer recommend Ubuntu Core or snaps for use in robotics.
My background is pretty heavily littered with robotics. A natural side effect of this is that I&amp;rsquo;ve published numerous posts discussing snaps, Ubuntu Core, and different robotics frameworks (ROS and MOOS specifically). But my robotics experience was professional, which meant I didn&amp;rsquo;t really have a reason (or money, these things can be expensive) to buy any robotic systems personally.</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;UPDATE: I&amp;rsquo;m leaving this series up for historical purposes, but please note that I no longer recommend Ubuntu Core or snaps for use in robotics.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;My background is pretty heavily littered with robotics. A natural side effect of this is that I&amp;rsquo;ve published numerous posts discussing snaps, Ubuntu Core, and different robotics frameworks (ROS and MOOS specifically). But my robotics experience was professional, which meant I didn&amp;rsquo;t really have a reason (or money, these things can be expensive) to buy any robotic systems personally. As a result, most of my posts have been pretty high level. Well today, that changes. Today, I have what you see above, which is a &lt;a href=&#34;http://www.turtlebot.com/&#34;&gt;Turtlebot 2&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Well, not really. What you see here is the iClebo Kobuki, which is the base of the Turtlebot 2. The barebones version of the Turtlebot 2 is the Kobuki plus some mounting hardware that goes on top. You can save some significant money if you can do without the mounting hardware: skip the kit, and &lt;a href=&#34;https://dabit.industries/products/iclebo-kobuki&#34;&gt;buy the Kobuki itself&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Anyway, the Turtlebot 2 is the go-to robotic platform for people learning ROS. Not just because there&amp;rsquo;s no time needed for researching components and writing the necessary software to build your own, but because this is what the ROS community has standardized around. It already has numerous ROS packages written for it, as well as its own set of excellent &lt;a href=&#34;http://wiki.ros.org/Robots/TurtleBot&#34;&gt;tutorials on the ROS wiki&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This situation makes it the perfect guinea pig for the ROS blog series I&amp;rsquo;ve been itching to write: taking your ROS prototype to production with snaps and Ubuntu Core. This series will cover the following topics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://kyrofa.com/posts/ros-production-our-prototype-2-5&#34;&gt;ROS production: our prototype [2/5]&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Creating an example prototype using classic Ubuntu and ROS Kinetic.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://kyrofa.com/posts/ros-production-our-prototype-as-a-snap-3-5&#34;&gt;ROS production: our prototype as a snap [3/5]&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Packaging that prototype as a snap.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://kyrofa.com/posts/ros-production-obtaining-confined-access-to-the-turtlebot-4-5&#34;&gt;ROS production: obtaining confined access to the Turtlebot [4/5]&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Creation of a gadget snap to allow explicit access to the Turtlebot.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://kyrofa.com/posts/ros-production-create-an-ubuntu-core-image-with-our-snap-preinstalled-5-5&#34;&gt;ROS production: create an Ubuntu Core image with our snap preinstalled [5/5]&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Creation of an image that is ready for the factory.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this first post, I want to discuss why Ubuntu Core is such a good fit for production robotics. Note that this is also a video series, feel free to watch the video version of this post:&lt;/p&gt;

&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
  &lt;iframe src=&#34;https://www.youtube-nocookie.com/embed/x6BkzfwOZbc&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; allowfullscreen title=&#34;YouTube Video&#34;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;ROS is great for research and development. Getting started is as simple as installing a few packages from the ROS archive, creating a workspace, and diving in. This is all I ever did in the past. When it was time to try my changes out on the real robotic system, I&amp;rsquo;d SSH into it, fetch my changes from git, and run the source on the robot. This is great for prototyping, but what happens when you&amp;rsquo;re ready to ship something?&lt;/p&gt;
&lt;p&gt;Going to production opens up a host of new concerns. You have to ask yourself a number of questions, such as:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;What is my factory process?&lt;/li&gt;
&lt;li&gt;How is the base OS updated so as to remain secure?&lt;/li&gt;
&lt;li&gt;How do I update the robotic software I have on top?&lt;/li&gt;
&lt;li&gt;How is the device recovered if an update goes wrong?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&amp;rsquo;s take a look at each of these questions in turn.&lt;/p&gt;
&lt;h2 id=&#34;1-what-is-your-factory-process&#34;&gt;1. What is your factory process?&lt;/h2&gt;
&lt;p&gt;In other words: how do you initially deploy Ubuntu, ROS, and your special sauce on a device you&amp;rsquo;re about to ship? By hand? That doesn&amp;rsquo;t scale and is error prone. Seed a custom Ubuntu distro with everything? That would actually work, but it&amp;rsquo;s not easy to maintain. I know some ROS users use Chef or another automation tool for this, but there&amp;rsquo;s a serious learning curve there. With Ubuntu Core, you&amp;rsquo;ll end up with a single flashable image that includes the operating system as well as your ROS system, all ready to go. I&amp;rsquo;ll show you how in this series.&lt;/p&gt;
&lt;h2 id=&#34;2-how-is-the-base-os-updated-so-as-to-remain-secure&#34;&gt;2. How is the base OS updated so as to remain secure?&lt;/h2&gt;
&lt;p&gt;Perhaps with your complex Chef setup you&amp;rsquo;ve also enabled automatic security updates. Good thinking, but there are other issues with this (see question 4).&lt;/p&gt;
&lt;p&gt;With Ubuntu Core, assuming you use one of the &lt;a href=&#34;https://developer.ubuntu.com/core/get-started&#34;&gt;reference devices&lt;/a&gt; (this includes just generic amd64) and don&amp;rsquo;t need to mess with the kernel, the kernel and core snaps are maintained by Canonical for the lifetime of the LTS. They&amp;rsquo;ll automatically update out of the box, too.&lt;/p&gt;
&lt;h2 id=&#34;3-how-do-i-update-the-robotic-software-i-have-on-top&#34;&gt;3. How do I update the robotic software I have on top?&lt;/h2&gt;
&lt;p&gt;A question to be answered first is: How is your software packaged? Is it packaged at all? Are you still running it from source? Do you have it on the ROS build farm? What if it isn&amp;rsquo;t public? Perhaps you use bloom and then build debs locally, but then you need to maintain your own archive infrastructure. None of these are fun solutions, nor do they lead to an easy upgrade process.&lt;/p&gt;
&lt;p&gt;If you package your ROS system as a snap, it includes all its dependencies (including its greater ROS dependencies). If your factory process included placing a snap on the robotic system, then it will automatically update. All you need to do is release the update in the store, and all the deployed devices with automatically update themselves.&lt;/p&gt;
&lt;h2 id=&#34;4-how-is-the-device-recovered-if-an-update-goes-wrong&#34;&gt;4. How is the device recovered if an update goes wrong?&lt;/h2&gt;
&lt;p&gt;My wife had this happen recently. She brought her laptop to me with a screen showing the grub prompt, unable to boot. After some poking, it turns out that &amp;ldquo;somehow&amp;rdquo; (she denies any knowledge of how this may have happened) the computer was forcibly powered off while the kernel was being updated (an automatic security update, no less). I manually told grub to boot the previous kernel, reinstalled the new one, and all was right with the world. But that would have been a lot harder if her laptop was actually a headless, production robot in my warehouse that I didn&amp;rsquo;t even know ran Linux.&lt;/p&gt;
&lt;p&gt;Ubuntu Core is based entirely upon &lt;a href=&#34;https://snapcraft.io/&#34;&gt;snaps&lt;/a&gt;, which are really just squashfs images containing software and their dependencies. This allows them to have some neat properties, one of which is that the upgrade process is as simple as downloading the new image, unmounting the old one, and mounting the new one into place (this is all done automatically, behind the scenes). Another one is that the old snap, once unmounted, is actually kept around, in case something goes wrong with the new snap. If a problem is detected, either by automated health checks, your own health checks within the snap, or in the case of a kernel, a panic, the snap will be reverted to the old one automatically. The atomic nature of snaps also means that either an installation succeeded or failed, there&amp;rsquo;s no intermediate state like what happened above to my wife&amp;rsquo;s laptop. If power is pulled in the middle of an update, snapd will remember that it was updating next time it boots, and finish the update.&lt;/p&gt;
&lt;p&gt;I hope I&amp;rsquo;ve given some good justification on why Ubuntu Core is a good fit for production robotics. The &lt;a href=&#34;https://kyrofa.com/posts/ros-production-our-prototype-2-5&#34;&gt;next post in this series&lt;/a&gt; will be all classic Ubuntu and ROS, discussing a basic prototype with the Turtlebot that we&amp;rsquo;ll use as an example throughout the rest of the series as we work toward production.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Distributing a ROS system among multiple snaps</title>
      <link>https://kyrofa.com/posts/distributing-a-ros-system-among-multiple-snaps/</link>
      <pubDate>Mon, 20 Mar 2017 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/distributing-a-ros-system-among-multiple-snaps/</guid>
      <description>One of the key tenets of snaps is that they bundle their dependencies. The fact that they&amp;rsquo;re self-contained helps their transactional-ness: upgrading or rolling back is essentially just a matter of unmounting one snap and mounting the other. However, historically this was also one of their key downsides: every snap must be standalone. Fortunately, snapd v2.0.10 saw the addition of a content interface that could be used by a producer snap to make its content available for use by a consumer snap.</description>
      <content:encoded>&lt;p&gt;One of the key tenets of snaps is that they bundle their dependencies. The fact that they&amp;rsquo;re self-contained helps their transactional-ness: upgrading or rolling back is essentially just a matter of unmounting one snap and mounting the other. However, historically this was also one of their key downsides: every snap must be standalone. Fortunately, snapd v2.0.10 saw the addition of a &lt;code&gt;content&lt;/code&gt; interface that could be used by a producer snap to make its content available for use by a consumer snap. However, that interface was very difficult to utilize when it came to ROS due to ROS&amp;rsquo;s use of workspaces for both building and running. At long last, support is landing in Snapcraft for building a ROS system that is distributed among multiple snaps, and I wanted to give you a preview of what that will look like.&lt;/p&gt;
&lt;h2 id=&#34;why-would-you-want-to-do-that&#34;&gt;Why would you want to do that?&lt;/h2&gt;
&lt;p&gt;Like I said, snaps bundling their dependencies is typically a good thing, and this applies to ROS-based snaps as well. Having an entire ROS system in a single snap that updates transactionally is awesome, useful for most deployment cases, and is generally what I recommend. However, there are some use-cases where this breaks down.&lt;/p&gt;
&lt;p&gt;For example, say I&amp;rsquo;m manufacturing an unmanned aerial vehicle. I want to sell it in such a state that it&amp;rsquo;s only capable of being piloted via remote control. This is done with a ROS system, which in a simple world would be made up of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One node to act as a driver for the RC radio&lt;/li&gt;
&lt;li&gt;One node to drive the motors&lt;/li&gt;
&lt;li&gt;Launch file to connect the two&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You get the idea. In addition to that basic platform, I want my users to be able to buy add-on packs. For example, perhaps the vehicle includes a GPS sensor (as well as basic pose sensors). I&amp;rsquo;d like to sell an add-on pack that adds a very basic &amp;ldquo;fly here&amp;rdquo; autopilot, or perhaps a &amp;ldquo;follow me&amp;rdquo; mode. That&amp;rsquo;s another ROS system, perhaps something like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One node to act as a driver for the GPS&lt;/li&gt;
&lt;li&gt;One node (or perhaps a few) to act as a driver for the pose sensors.&lt;/li&gt;
&lt;li&gt;One node to plan a path&lt;/li&gt;
&lt;li&gt;One node to take the path and turn it into motor controls&lt;/li&gt;
&lt;li&gt;A launch file to bring up this system&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If we build both of these snaps to be standalone, we quickly run into issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lots of duplication between them, as the autopilot snap will need to include most of the base behavior snap.&lt;/li&gt;
&lt;li&gt;They both include (and will try to launch) their own roscore.&lt;/li&gt;
&lt;li&gt;The duplicated snaps in each will try to access their respective hardware. This is a race condition: the first one up will win, the second will die. Or, depending on the hardware interface, they&amp;rsquo;ll both control it. That&amp;rsquo;s fun.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Using content sharing, we can actually make the autopilot snap depend upon and utilize the base behavior snap.&lt;/p&gt;
&lt;h2 id=&#34;alright-what-does-this-look-like&#34;&gt;Alright, what does this look like?&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s simplify our previous example into two snaps: a &amp;ldquo;ros-base&amp;rdquo; snap that includes the typical stuff: &lt;code&gt;roscore&lt;/code&gt;, &lt;code&gt;roslaunch&lt;/code&gt;, etc., and a &amp;ldquo;ros-app&amp;rdquo; snap that includes packages that actually do something, specifically the classic talker/listener example. A quick reminder: &lt;strong&gt;this will only be possible in Snapcraft v2.28 or later&lt;/strong&gt;. Also note that the example I&amp;rsquo;m about to walk through is a demo in Snapcraft, in case you want to look at the whole thing.&lt;/p&gt;
&lt;h2 id=&#34;create-ros-base&#34;&gt;Create &lt;code&gt;ros-base&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;(&lt;strong&gt;Note added after the fact:&lt;/strong&gt; The &amp;ldquo;base&amp;rdquo; terminology here predates snap bases, a concept introduced for core18. If it was writing this post today, I&amp;rsquo;d use the word &amp;ldquo;foundation&amp;rdquo; instead of &amp;ldquo;base&amp;rdquo; to avoid the clash. That&amp;rsquo;s really the only core/base/foundational-type word not yet claimed by the snap ecosystem, heh. Anyway, don&amp;rsquo;t get confused here.)&lt;/p&gt;
&lt;p&gt;To create the base snap, create a &lt;code&gt;snap/snapcraft.yaml&lt;/code&gt; file with the following contents:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ros-base&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1.0&amp;#39;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;grade&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;stable&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;confinement&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;strict&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;summary&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ROS Base Snap&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;description&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Contains roscore and basic ROS utilities.&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;slots&lt;/span&gt;:
 &lt;span style=&#34;color:#75715e&#34;&gt;# This is how we make a part of this snap readable by other snaps.&lt;/span&gt;
 &lt;span style=&#34;color:#75715e&#34;&gt;# Consumers will need to access the PYTHONPATH as well as various libs&lt;/span&gt;
 &lt;span style=&#34;color:#75715e&#34;&gt;# contained in this snap, so share the entire $SNAP, not just the ROS&lt;/span&gt;
 &lt;span style=&#34;color:#75715e&#34;&gt;# workspace.&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;ros-base&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;content&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ros-base-v1&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;interface&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;content&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;read&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;/]&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;parts&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;ros-base&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;catkin&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;rosdistro&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;kinetic&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;include-roscore&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;catkin-packages&lt;/span&gt;: []
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. Run &lt;code&gt;snapcraft&lt;/code&gt; on it, and after a little time you&amp;rsquo;ll have your base snap (the &amp;ldquo;provider&amp;rdquo; snap regarding content sharing). This particular example doesn&amp;rsquo;t do a whole lot by itself, so let&amp;rsquo;s move on to our &lt;code&gt;ros-app&lt;/code&gt; snap (the &amp;ldquo;consumer&amp;rdquo; snap regarding content sharing).&lt;/p&gt;
&lt;h2 id=&#34;create-ros-app&#34;&gt;Create &lt;code&gt;ros-app&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The starting point for &lt;code&gt;ros-app&lt;/code&gt; is the &lt;a href=&#34;https://github.com/snapcore/snapcraft/tree/2.34/demos/ros&#34;&gt;current standalone ROS demo&lt;/a&gt;. We&amp;rsquo;ll use the exact same ROS workspace, but we&amp;rsquo;ll add a few more things and tweak the YAML a bit.&lt;/p&gt;
&lt;p&gt;The recommended way to build a &amp;ldquo;consumer&amp;rdquo; snap (assuming it has a build-time dependency on the content shared from the &amp;ldquo;producer&amp;rdquo; snap, which &lt;code&gt;ros-app&lt;/code&gt; does indeed have on &lt;code&gt;ros-base&lt;/code&gt;) is to create a tarball of the producer&amp;rsquo;s staging area, and use it as a part to build the consumer.&lt;/p&gt;
&lt;p&gt;Concretely, we can tar up the staging area of &lt;code&gt;ros-base&lt;/code&gt; and use it to build &lt;code&gt;ros-app&lt;/code&gt;, but then filter it out of the final &lt;code&gt;ros-app&lt;/code&gt; snap (so as to not duplicate the contents of &lt;code&gt;ros-base&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;So let&amp;rsquo;s do that now. Get into the directory containing the now-built &lt;code&gt;ros-base&lt;/code&gt; snap, &lt;code&gt;tar&lt;/code&gt; up its staging area, then move it off into the &lt;code&gt;ros-app&lt;/code&gt; area:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ tar czf ros-base.tar.bz2 stage/
$ mv ros-base.tar.bz2 /path/to/ros-app
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, in &lt;code&gt;/path/to/ros-app&lt;/code&gt; alter the &lt;code&gt;snap/snapcraft.yaml&lt;/code&gt; to look something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ros-app&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1.0&amp;#39;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;grade&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;stable&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;confinement&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;strict&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;summary&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ROS App Snap&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;description&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Contains talker/listener ROS packages and a .launch file.&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;plugs&lt;/span&gt;:
 &lt;span style=&#34;color:#75715e&#34;&gt;# Mount the content shared from ros-base into $SNAP/ros-base&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;ros-base&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;content&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ros-base-v1&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;interface&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;content&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;target&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;/ros-base&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;apps&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;launch-project&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;command&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;run-system&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;plugs&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;network, network-bind, ros-base]&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;parts&lt;/span&gt;:
 &lt;span style=&#34;color:#75715e&#34;&gt;# The `source` here is the tarred staging area of the ros-base snap.&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;ros-base&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;dump&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;source&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ros-base.tar.bz2&lt;/span&gt;
   &lt;span style=&#34;color:#75715e&#34;&gt;# This is only used for building-- filter it out of the final snap.&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;prime&lt;/span&gt;: [-&lt;span style=&#34;color:#75715e&#34;&gt;*]&lt;/span&gt;

 &lt;span style=&#34;color:#75715e&#34;&gt;# This is mostly unchanged from the standalone ROS example. Notable&lt;/span&gt;
 &lt;span style=&#34;color:#75715e&#34;&gt;# additions are:&lt;/span&gt;
 &lt;span style=&#34;color:#75715e&#34;&gt;# - Using Kinetic now (other demo is Indigo)&lt;/span&gt;
 &lt;span style=&#34;color:#75715e&#34;&gt;# - Specifically not including roscore&lt;/span&gt;
 &lt;span style=&#34;color:#75715e&#34;&gt;# - Making sure we&amp;#39;re building AFTER our underlay&lt;/span&gt;
 &lt;span style=&#34;color:#75715e&#34;&gt;# - Specifying the build- and run-time paths of the underlay&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;ros-app&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;catkin&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;rosdistro&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;kinetic&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;include-roscore&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;underlay&lt;/span&gt;:
     &lt;span style=&#34;color:#75715e&#34;&gt;# Build-time location of the underlay&lt;/span&gt;
     &lt;span style=&#34;color:#f92672&#34;&gt;build-path&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;$SNAPCRAFT_STAGE/opt/ros/kinetic&lt;/span&gt;

     &lt;span style=&#34;color:#75715e&#34;&gt;# Run-time location of the underlay&lt;/span&gt;
     &lt;span style=&#34;color:#f92672&#34;&gt;run-path&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;$SNAP/ros-base/opt/ros/kinetic&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;catkin-packages&lt;/span&gt;:
     - &lt;span style=&#34;color:#ae81ff&#34;&gt;talker&lt;/span&gt;
     - &lt;span style=&#34;color:#ae81ff&#34;&gt;listener&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;after&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;ros-base]&lt;/span&gt;

 &lt;span style=&#34;color:#75715e&#34;&gt;# We can&amp;#39;t just use roslaunch now, since that&amp;#39;s contained in the&lt;/span&gt;
 &lt;span style=&#34;color:#75715e&#34;&gt;# underlay. This part will tweak the environment a little to&lt;/span&gt;
 &lt;span style=&#34;color:#75715e&#34;&gt;# utilize the underlay.&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;run-system&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;dump&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;stage&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;bin/run-system]&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;prime&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;bin/run-system]&lt;/span&gt;

 &lt;span style=&#34;color:#75715e&#34;&gt;# We need to create the $SNAP/ros-base mountpoint for the content&lt;/span&gt;
 &lt;span style=&#34;color:#75715e&#34;&gt;# being shared.&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;mountpoint&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;nil&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;install&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;mkdir $SNAPCRAFT_PART_INSTALL/ros-base&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Other than the ROS workspace in &lt;code&gt;src/&lt;/code&gt; (which remains unchanged from the other demo so we won&amp;rsquo;t discuss it here), we need to create a &lt;code&gt;bin/run-system&lt;/code&gt; executable that looks something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# Would sure be nice if snapd gave us the triplet as well.&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# It doesn&amp;#39;t, so we&amp;#39;ll just create it here. I&amp;#39;m only adding&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# support for amd64 here, but one could add this logic for&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# any arch they wanted (assuming ROS builds there, of&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# course).&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; $SNAP_ARCH in
amd64&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
   export TRIPLET&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;x86_64-linux-gnu
   ;;
*&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
   echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Unsupported arch: &lt;/span&gt;$SNAP_ARCH&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
   exit &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
   ;;
&lt;span style=&#34;color:#66d9ef&#34;&gt;esac&lt;/span&gt;

export ROS_BASE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$SNAP/ros-base

&lt;span style=&#34;color:#75715e&#34;&gt;# Add ros-base to the PYTHONPATH&lt;/span&gt;
export PYTHONPATH&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$PYTHONPATH:$ROS_BASE/usr/lib/python2.7/dist-packages

&lt;span style=&#34;color:#75715e&#34;&gt;# Add ros-base to LD_LIBRARY_PATH&lt;/span&gt;
export LD_LIBRARY_PATH&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$LD_LIBRARY_PATH:$ROS_BASE/lib
export LD_LIBRARY_PATH&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$LD_LIBRARY_PATH:$ROS_BASE/lib/$TRIPLET
export LD_LIBRARY_PATH&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$LD_LIBRARY_PATH:$ROS_BASE/usr/lib
export LD_LIBRARY_PATH&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$LD_LIBRARY_PATH:$ROS_BASE/usr/lib/$TRIPLET

roslaunch listener talk_and_listen.launch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Why is this needed? Because the Catkin plugin can only do so much for you. The &lt;code&gt;ros-base&lt;/code&gt; snap includes various python modules and libs outside of its ROS workspace that &lt;code&gt;ros-app&lt;/code&gt; needs, so we extend the &lt;code&gt;$PYTHONPATH&lt;/code&gt; and &lt;code&gt;$LD_LIBRARY_PATH&lt;/code&gt; to utilize them.&lt;/p&gt;
&lt;p&gt;From there, it&amp;rsquo;s as easy as running &lt;code&gt;roslaunch&lt;/code&gt; (which by the way is contained in &lt;code&gt;ros-base&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;snapcraft&lt;/code&gt; on this, and after a few minutes (fairly quick since it&amp;rsquo;s re-using the base&amp;rsquo;s staging area to build) you&amp;rsquo;ll have a ros-app snap.&lt;/p&gt;
&lt;h2 id=&#34;so-now-i-have-two-ros-snaps-now-what&#34;&gt;So now I have two ROS snaps. Now what?&lt;/h2&gt;
&lt;p&gt;You now have your ROS system split between multiple snaps. The first step is to install both snaps:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install --dangerous ros-base_1.0_amd64.snap
ros-base 1.0 installed
$ sudo snap install --dangerous ros-app_1.0_amd64.snap
ros-app 1.0 installed
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now take a look at &lt;code&gt;snap interfaces&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snap interfaces
Slot                     Plug
ros-base:ros-base        -
:alsa                    -
:avahi-observe           -
...
&amp;lt;snip&amp;gt;
...
-                        ros-app:ros-base
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;ll see that &lt;code&gt;ros-base:ros-base&lt;/code&gt; is an available slot, and &lt;code&gt;ros-app:ros-base&lt;/code&gt; is an available plug. This interface is currently not connected, so content sharing is not yet taking place. Let&amp;rsquo;s connect them:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap connect ros-app:ros-base ros-base:ros-base
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Taking another look at &lt;code&gt;snap interfaces&lt;/code&gt; you can see they&amp;rsquo;re now connected:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snap interfaces
Slot                     Plug
ros-base:ros-base        ros-app
:alsa                    -
:avahi-observe           -
...
&amp;lt;snip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And now you can launch this ROS system you now have distributed between two snaps:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ ros-app.launch-project
&amp;lt;snip&amp;gt;
NODES
 /
   listener &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;listener/listener_node&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
   talker &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;talker/talker_node&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&amp;lt;snip&amp;gt;
process&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;talker-2&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: started with pid &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;10649&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
process&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;listener-3&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: started with pid &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;10650&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1487121136.757225517&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1487121136.860879281&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1487121136.960885723&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1487121137.057481265&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1487121137.058298&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: I heard Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;
&amp;lt;snip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Multiple ROS users have mentioned that the fact that a ROS snap must be completely self-contained is a problem. Typically it either interferes with their workflow or their business plan. We&amp;rsquo;ve heard you! We can&amp;rsquo;t pretend that the snap world of isolated blobs and the ROS world of workspaces merge perfectly, but the content interface takes a big step toward blending these two worlds, and the new features in Snapcraft&amp;rsquo;s Catkin plugin hopefully makes it as easy as possible to utilize.&lt;/p&gt;
&lt;p&gt;I personally look forward to seeing what you do with this!&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>ROS on arm64 with Ubuntu Core</title>
      <link>https://kyrofa.com/posts/ros-on-arm64-with-ubuntu-core/</link>
      <pubDate>Thu, 26 Jan 2017 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/ros-on-arm64-with-ubuntu-core/</guid>
      <description>Previous Robot Operating System (ROS) releases only supported i386, amd64, and armhf. I even tried building ROS Indigo from source for arm64 about a year ago, but ran into dependency issues with a missing sbcl. Well, with surprisingly little fanfare, ROS Kinetic was released with support for arm64 in their prebuilt archive! I thought it might be time to take it for a spin with Ubuntu Core and its arm64 reference board, the DragonBoard 410c, and thought I&amp;rsquo;d take you along for the ride.</description>
      <content:encoded>&lt;p&gt;Previous Robot Operating System (ROS) releases only supported i386, amd64, and armhf. I even tried building ROS Indigo from source for arm64 about a year ago, but ran into dependency issues with a missing &lt;code&gt;sbcl&lt;/code&gt;. Well, with surprisingly little fanfare, ROS Kinetic was released with support for arm64 in their prebuilt archive! I thought it might be time to take it for a spin with Ubuntu Core and its arm64 reference board, the &lt;a href=&#34;https://developer.qualcomm.com/hardware/dragonboard-410c&#34;&gt;DragonBoard 410c&lt;/a&gt;, and thought I&amp;rsquo;d take you along for the ride.&lt;/p&gt;
&lt;h2 id=&#34;step-1-install-ubuntu-core&#34;&gt;Step 1: Install Ubuntu Core&lt;/h2&gt;
&lt;p&gt;The first order of business is to install Ubuntu Core on the DragonBoard. Just &lt;a href=&#34;https://ubuntu.com/download/iot/qualcomm-dragonboard-410c&#34;&gt;follow the documentation&lt;/a&gt;. I want to mention two things for this step, though:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I&amp;rsquo;ve never had good luck with wifi on the DragonBoard, it seems incredibly unstable. I recommend not even bothering with it and using a USB to ethernet adapter, since with Ubuntu Core at least your first login must be over SSH.&lt;/li&gt;
&lt;li&gt;There&amp;rsquo;s a &lt;a href=&#34;https://bugs.launchpad.net/snappy/+bug/1638537&#34;&gt;bug&lt;/a&gt; that causes the first boot wizard to take quite some time between entering your SSO password and finishing. Don&amp;rsquo;t worry, just leave it alone, it&amp;rsquo;ll finish (mine took about 7 minutes).&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;step-2-make-sure-its-up-to-date&#34;&gt;Step 2: Make sure it&amp;rsquo;s up-to-date&lt;/h2&gt;
&lt;p&gt;SSH into the machine (or if you set a password, login locally, it doesn&amp;rsquo;t matter), and run the following command to ensure everything is completely up-to-date:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap refresh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If it updated, you may need to reboot. Go ahead and do that now, we&amp;rsquo;ll wait.&lt;/p&gt;
&lt;h2 id=&#34;step-3-install-snapcraft&#34;&gt;Step 3: Install Snapcraft&lt;/h2&gt;
&lt;p&gt;You may or may not be familiar with Ubuntu Core, but the first thing people typically notice is that it doesn&amp;rsquo;t use Debian packages (.debs), it uses snaps (read up on them, they&amp;rsquo;re pretty awesome). However, in this case, they don&amp;rsquo;t give us what we need, which is a development environment. We need to build a ROS workspace into a snap, which means we&amp;rsquo;ll need to utilize ROS&amp;rsquo;s archive as well as the snap packaging tool, snapcraft. All of these are available as .debs. Fortunately, &lt;a href=&#34;https://developer.ubuntu.com/core/get-started/developer-setup&#34;&gt;the environment we want is available as a snap&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snap install classic --edge --devmode
$ sudo classic
&amp;lt;unpacking stuff... snip&amp;gt;
&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;classic&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;kyrofa@localhost:~$
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;(classic)&lt;/code&gt; prompt modifier tells you that you&amp;rsquo;re now in a classic shell. Now you can do familiar things such as update the package index and install Snapcraft, both of which you should do now:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;classic&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;kyrofa@localhost:~$ sudo apt update
&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;classic&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;kyrofa@localhost:~$ sudo apt install snapcraft
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;step-4-build-your-snap&#34;&gt;Step 4: Build your snap&lt;/h2&gt;
&lt;p&gt;Probably the most simple ROS workspace you can imagine is a single talker and a single listener. Snapcraft has exactly that as one of its demos, so for this example we&amp;rsquo;ll just use that (you might consider reading that demo&amp;rsquo;s &lt;a href=&#34;https://github.com/snapcore/snapcraft/blob/2.28/docs/ros-snap.md&#34;&gt;walkthrough&lt;/a&gt;). First though, that means fetching the Snapcraft source code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;classic&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;kyrofa@localhost:~$ sudo apt install git
&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;classic&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;kyrofa@localhost:~$ git clone --branch 2.28 https://github.com/snapcore/snapcraft
&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;classic&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;kyrofa@localhost:~$ cd snapcraft/demos/ros
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The ROS demo in question will by default actually build for Indigo. But you&amp;rsquo;re interested in arm64! You can&amp;rsquo;t use Indigo. You need something newer&amp;hellip; something shinier, and perhaps less purple. You need to use Kinetic. You tell Snapcraft this by utilizing the &lt;code&gt;rosdistro&lt;/code&gt; property. Open up the snapcraft.yaml and edit the appropriate section to look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;...&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
parts:
 ros-project:
   plugin: catkin
   source: .
   rosdistro: kinetic
   catkin-packages:
     - talker
     - listener
   include-roscore: true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Save that file and exit. Finally, it&amp;rsquo;s time to run &lt;code&gt;snapcraft&lt;/code&gt; and watch that workspace get built into a snap (this will take a bit, the DragonBoard is not a blazing workhorse; if you want something faster use the &lt;a href=&#34;https://kyrofa.com/posts/building-your-snap-on-device-there-s-a-better-way&#34;&gt;snap builders&lt;/a&gt;):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;classic&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;kyrofa@localhost:~/snapcraft/demos/ros$ snapcraft
&amp;lt;snip pulling, building, staging&amp;gt;
Priming ros-project
Snapping &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ros-example&amp;#39;&lt;/span&gt;
Snapped ros-example_1.0_arm64.snap
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the end you have a snap. You should exit out of the classic shell at this point (e.g. ctrl+d).&lt;/p&gt;
&lt;h2 id=&#34;step-5-install-and-run-it&#34;&gt;Step 5: Install and run it!&lt;/h2&gt;
&lt;p&gt;Now it&amp;rsquo;s time to install the snap you just built. Even though you&amp;rsquo;ve exited the classic shell, your &lt;code&gt;$HOME&lt;/code&gt; remained the same, so you can install the snap like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd snapcraft/demos/ros
$ sudo snap install --dangerous ros-example_1.0_arm64.snap
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(remember the &lt;code&gt;--dangerous&lt;/code&gt; flag is because you just built the snap locally, so snapd can&amp;rsquo;t verify its publisher. You&amp;rsquo;re telling it that&amp;rsquo;s okay.)&lt;/p&gt;
&lt;p&gt;Finally, run the application contained within the snap (it&amp;rsquo;ll launch the talker/listener system):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ ros-example.launch-project
&amp;lt;snip&amp;gt;
SUMMARY
&lt;span style=&#34;color:#f92672&#34;&gt;========&lt;/span&gt;
PARAMETERS
 * /rosdistro: kinetic
 * /rosversion: 1.12.6
NODES
 /
   listener &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;listener/listener_node&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
   talker &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;talker/talker_node&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;

auto-starting new master
&amp;lt;snip&amp;gt;
process&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;talker-2&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: started with pid &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;25827&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
process&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;listener-3&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: started with pid &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;25828&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1485394763.340461416&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1485394763.440354547&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1485394763.540334917&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1485394763.640330599&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1485394763.740335917&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1485394763.840366912&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1485394763.940342594&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1485394764.040321141&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1485394764.140323334&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1485394764.240328548&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1485394764.340319074&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1485394764.341486&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: I heard Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1485394764.440333194&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1485394764.441476&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: I heard Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1485394764.540333772&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;12&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;INFO&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1485394764.541450&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;: I heard Hello world &lt;span style=&#34;color:#ae81ff&#34;&gt;12&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I haven&amp;rsquo;t yet pushed ROS&amp;rsquo;s arm64 support very hard, but I&amp;rsquo;m thoroughly pleased that support is present. Particularly paired with snaps and Ubuntu Core, I think this opens the door to a lot of amazing robotic possibilities.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Snap updates: automatic rollbacks</title>
      <link>https://kyrofa.com/posts/snap-updates-automatic-rollbacks/</link>
      <pubDate>Mon, 12 Dec 2016 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/snap-updates-automatic-rollbacks/</guid>
      <description>When researching snaps, one of the main advantages everyone talks about is the fact that they&amp;rsquo;re transactionally updated. That is, an upgrade either succeeds or fails, it doesn&amp;rsquo;t leave the snap in a broken state. If you have snap &amp;ldquo;A&amp;rdquo; installed and an update for it is released, it&amp;rsquo;ll automatically update. If that update is somehow broken, the snap will roll back to the previously-working revision. However, no one has really talked about how that works.</description>
      <content:encoded>&lt;p&gt;When researching snaps, one of the main advantages everyone talks about is the fact that they&amp;rsquo;re transactionally updated. That is, an upgrade either succeeds or fails, it doesn&amp;rsquo;t leave the snap in a broken state. If you have snap &amp;ldquo;A&amp;rdquo; installed and an update for it is released, it&amp;rsquo;ll automatically update. If that update is somehow broken, the snap will roll back to the previously-working revision. However, no one has really talked about how that works. How does a snap know that an update is broken? That&amp;rsquo;s what I want to talk about today.&lt;/p&gt;
&lt;p&gt;Snapd won&amp;rsquo;t check the health of your snap for you, as it doesn&amp;rsquo;t know enough about your snap to perform a decent health check. However, it&amp;rsquo;s pretty easy to check the health yourself.&lt;/p&gt;
&lt;p&gt;Snapd recently gained support for &amp;ldquo;hooks&amp;rdquo;, which is a way for snapd to notify individual snaps about various events. There&amp;rsquo;s one hook in particular that supports &lt;a href=&#34;https://docs.snapcraft.io/supported-snap-hooks#heading--the-configure-hook&#34;&gt;configuration&lt;/a&gt;. It&amp;rsquo;s beyond the scope of this post to walk through configuration itself, but there&amp;rsquo;s something important about this hook that&amp;rsquo;s worth knowing: it runs upon initial install, and it runs when the snap is &lt;em&gt;updated&lt;/em&gt;, after the snap&amp;rsquo;s services are started. This makes it a decent place to perform a health check. (&lt;strong&gt;Update:&lt;/strong&gt; there are probably better hooks for this now, such as the &lt;code&gt;pre-refresh&lt;/code&gt; hook, but that doesn&amp;rsquo;t change how to use them)&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d like to explain with an example, but first we need to make sure we&amp;rsquo;re on the same page. This support is pretty new as of this writing, and you need to be running the right version of snapd.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;First of all, verify that you&amp;rsquo;re at least on Xenial running version 2.17.1ubuntu1:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snap --version
snap   2.17.1ubuntu1
snapd  2.17.1ubuntu1
series &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt;
ubuntu 16.04
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Also make sure your core snap (it could also be called ubuntu-core, that&amp;rsquo;s okay) is up to date (hook support was only recently added):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap refresh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;now-get-on-with-it&#34;&gt;Now get on with it&lt;/h2&gt;
&lt;p&gt;Now that we&amp;rsquo;re on the same page, let&amp;rsquo;s continue with our example. &lt;a href=&#34;https://docs.snapcraft.io/supported-snap-hooks&#34;&gt;Hooks&lt;/a&gt; are simply executables contained within the &lt;code&gt;meta/hooks/&lt;/code&gt; directory inside the snap. Snapcraft doesn&amp;rsquo;t yet have explicit support for hooks (&lt;strong&gt;Update:&lt;/strong&gt; it does now), but we can bend it to our will for this example.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s first create a directory for our new snap, and create the &lt;code&gt;snapcraft.yaml&lt;/code&gt; for it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ mkdir rollback-test
$ cd rollback-test &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; snapcraft init
Created snapcraft.yaml.
Edit the file to your liking or run &lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;snapcraft&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt; to get started
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Edit that &lt;code&gt;snapcraft.yaml&lt;/code&gt; and make it look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;rollback-test&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1.0&amp;#39;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;summary&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Snap to test rollback functionality.&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;description&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;You heard me.&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;grade&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;stable&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;confinement&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;strict&lt;/span&gt;

&lt;span style=&#34;color:#f92672&#34;&gt;parts&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;hook&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;dump&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;source&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now create a new file: &lt;code&gt;meta/hooks/configure&lt;/code&gt;. The tree should look like this when you&amp;rsquo;re done:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ tree
.
├── meta
│  └── hooks
│      └── configure
└── snapcraft.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Edit the &lt;code&gt;meta/hooks/configure&lt;/code&gt; file and make it look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/sh
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;This will succeed.&amp;#34;&lt;/span&gt;
exit &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Make that file executable (hooks must be executable):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ chmod a+x meta/hooks/configure
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And finally, create the snap:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft
Preparing to pull hook
Pulling hook
Preparing to build hook
Building hook
Staging hook
Priming hook
Snapping &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;rollback-test&amp;#39;&lt;/span&gt; |
Snapped rollback-test_1.0_amd64.snap
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you have a snap containing a configure hook that does exactly nothing other than exiting successfully. Install the snap:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install --dangerous rollback-test_1.0_amd64.snap
rollback-test 1.0 installed

$ snap list
Name          Version Rev Developer Notes
core          16.04.1 &lt;span style=&#34;color:#ae81ff&#34;&gt;641&lt;/span&gt; canonical -
rollback-test 1.0     x1            -
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Take a careful look at the revision for the snap we just installed: &lt;code&gt;x1&lt;/code&gt;. As you may know, revision numbers are assigned by the store. However, this snap didn&amp;rsquo;t come from the store: we just created and installed it locally. Snapd knows working with revisions can be handy here, so it makes one up for you: The &amp;ldquo;x&amp;rdquo; indicates that it was a local snap, and the &amp;ldquo;1&amp;rdquo; indicates that it&amp;rsquo;s been installed once. You could actually install it again over the top of it and get the revision to increment:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install --dangerous rollback-test_1.0_amd64.snap
rollback-test 1.0 installed

$ snap list
Name          Version Rev Developer Notes
core          16.04.1 &lt;span style=&#34;color:#ae81ff&#34;&gt;641&lt;/span&gt; canonical -
rollback-test 1.0     x2            -
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can see this works like an update from the store. You&amp;rsquo;ll notice that you now have two revisions on your disk (with only one active, of course):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ ls -l /snap/rollback-test/
total &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
lrwxrwxrwx &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; root root &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt; Dec &lt;span style=&#34;color:#ae81ff&#34;&gt;12&lt;/span&gt; 12:47 current -&amp;gt; x2
drwxrwxr-x &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt; root root &lt;span style=&#34;color:#ae81ff&#34;&gt;27&lt;/span&gt; Dec &lt;span style=&#34;color:#ae81ff&#34;&gt;12&lt;/span&gt; 12:42 x1
drwxrwxr-x &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt; root root &lt;span style=&#34;color:#ae81ff&#34;&gt;27&lt;/span&gt; Dec &lt;span style=&#34;color:#ae81ff&#34;&gt;12&lt;/span&gt; 12:42 x2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Alright, let&amp;rsquo;s get to the cool part. You probably didn&amp;rsquo;t notice it, but your &lt;code&gt;configure&lt;/code&gt; hook did actually run as part of the build step. Check this out:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snap changes
ID  Status Spawn  Ready  Summary
&lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;   Done   &amp;lt;snip&amp;gt; &amp;lt;snip&amp;gt; Install &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rollback-test&amp;#34;&lt;/span&gt; snap &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;...&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;   Done   &amp;lt;snip&amp;gt; &amp;lt;snip&amp;gt; Install &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rollback-test&amp;#34;&lt;/span&gt; snap &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;...&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I&amp;rsquo;ve cleaned the output up a little, but you get the idea. You see two changes there corresponding to the two back-to-back installs we did. Let&amp;rsquo;s take a closer look at that most recent change:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snap change &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;
Status Spawn   Ready Summary
Done   &amp;lt;snip&amp;gt; &amp;lt;snip&amp;gt; Prepare snap &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/tmp/snapd-sideload-pkg-798830678&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;unset&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
Done   &amp;lt;snip&amp;gt; &amp;lt;snip&amp;gt; Mount snap &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rollback-test&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;unset&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
Done   &amp;lt;snip&amp;gt; &amp;lt;snip&amp;gt; Stop snap &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rollback-test&amp;#34;&lt;/span&gt; services
Done   &amp;lt;snip&amp;gt; &amp;lt;snip&amp;gt; Make current revision &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; snap &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rollback-test&amp;#34;&lt;/span&gt; unavailable
Done   &amp;lt;snip&amp;gt; &amp;lt;snip&amp;gt; Copy snap &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rollback-test&amp;#34;&lt;/span&gt; data
Done   &amp;lt;snip&amp;gt; &amp;lt;snip&amp;gt; Setup snap &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rollback-test&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;unset&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; security profiles
Done   &amp;lt;snip&amp;gt; &amp;lt;snip&amp;gt; Make snap &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rollback-test&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;unset&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; available to the system
Done   &amp;lt;snip&amp;gt; &amp;lt;snip&amp;gt; Start snap &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rollback-test&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;unset&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; services
Done   &amp;lt;snip&amp;gt; &amp;lt;snip&amp;gt; Clean up &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rollback-test&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;unset&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; install
Done   &amp;lt;snip&amp;gt; &amp;lt;snip&amp;gt; Run configure hook of &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rollback-test&amp;#34;&lt;/span&gt; snap &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; present
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice that last step: &amp;ldquo;Run configure hook [&amp;hellip;] if present&amp;rdquo;. So you can see that the snap&amp;rsquo;s services start up, and then the &lt;code&gt;configure&lt;/code&gt; hook runs. This means that your &lt;code&gt;configure&lt;/code&gt; hook can make sure your services are running correctly, etc. But how would it tell snapd if something is wrong, i.e. the snap needs to be rolled back? That&amp;rsquo;s easy: fail.&lt;/p&gt;
&lt;p&gt;Alter your &lt;code&gt;meta/hooks/configure&lt;/code&gt; hook and make it look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/sh
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;This will fail.&amp;#34;&lt;/span&gt;
exit &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice we&amp;rsquo;re exiting non-zero here&amp;ndash; the hook will fail. Rebuild your snap so that it includes the new hook:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snapcraft clean
Cleaning priming area &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; hook 
Cleaning staging area &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; hook 
Cleaning build &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; hook 
Cleaning pulled source &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; hook 
Cleaning up snapping area

$ snapcraft
Preparing to pull hook 
Pulling hook 
Preparing to build hook 
Building hook 
Staging hook 
Priming hook 
Snapping &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;rollback-test&amp;#39;&lt;/span&gt; |
Snapped rollback-test_1.0_amd64.snap
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now try to install it again:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install --dangerous rollback-test_1.0_amd64.snap
error: cannot perform the following tasks:
- Run configure hook of &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rollback-test&amp;#34;&lt;/span&gt; snap &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; present &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;This will fail.&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Neat, huh? The failing hook actually made the installation fail. What does that entail, then? What is the state of the snap? See for yourself:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ snap list
Name          Version Rev Developer Notes
core          16.04.1 &lt;span style=&#34;color:#ae81ff&#34;&gt;641&lt;/span&gt; canonical -
rollback-test 1.0     x2            -
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If the installation succeeded, we&amp;rsquo;d be on revision x3. However, the installation failed, so snapd took action: it went back to the revision it knew was previously working: x2.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I obviously walked through this example locally without touching the store, but the &lt;em&gt;exact same principle applies&lt;/em&gt; if the snap is coming from the store and the update is caused automatically. If you published the first snap we made, and then published the second version of it as an update, the update would have reverted just like it did in our example. Give it a try yourself!&lt;/p&gt;
&lt;p&gt;Admittedly this example was simplistic. However, hopefully you can see how this would expand to a snap-wide health check. Since the &lt;code&gt;configure&lt;/code&gt; hook runs after the snap&amp;rsquo;s services are fired up, you can communicate with them, verify they&amp;rsquo;re running correctly, and exit non-zero if you detect a problem. This would allow snapd to take remedial action and undo the upgrade.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Ubuntu Core on VirtualBox</title>
      <link>https://kyrofa.com/posts/ubuntu-core-on-virtualbox/</link>
      <pubDate>Mon, 21 Nov 2016 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/ubuntu-core-on-virtualbox/</guid>
      <description>If you read the official Ubuntu Core docs, you&amp;rsquo;ll see that the recommended way to run Ubuntu Core virtually is with KVM. However, it&amp;rsquo;s probably even easier to use it in VirtualBox, and I&amp;rsquo;ve been getting some questions about that, so I wanted to devote a quick post to it.
Prerequisites:
 A Linux machine (using Ubuntu 16.04 here). This guide will work on Windows as well, but the command invocations are a bit different and I don&amp;rsquo;t have a machine on which I can verify them before suggesting them.</description>
      <content:encoded>&lt;p&gt;If you read the official Ubuntu Core docs, you&amp;rsquo;ll see that the recommended way to run Ubuntu Core virtually is with KVM. However, it&amp;rsquo;s probably even easier to use it in VirtualBox, and I&amp;rsquo;ve been getting some questions about that, so I wanted to devote a quick post to it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A Linux machine (using Ubuntu 16.04 here). This guide will work on Windows as well, but the command invocations are a bit different and I don&amp;rsquo;t have a machine on which I can verify them before suggesting them.&lt;/li&gt;
&lt;li&gt;VirtualBox installed and running successfully.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;guide&#34;&gt;Guide&lt;/h2&gt;
&lt;p&gt;First of all, download the appropriate Ubuntu Core 16 image from &lt;a href=&#34;http://cdimage.ubuntu.com/ubuntu-core/16/stable/&#34;&gt;here&lt;/a&gt;. I&amp;rsquo;m running 64-bit, so that&amp;rsquo;s the one I selected. Download it, and uncompress it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ wget http://cdimage.ubuntu.com/ubuntu-core/16/stable/current/ubuntu-core-16-amd64.img.xz
$ unxz ubuntu-core-16-amd64.img.xz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You now have a raw disk image. To make it usable within VirtualBox, convert it to a VDI:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ VBoxManage convertdd ubuntu-core-16-amd64.img ubuntu-core-16-amd64.vdi --format VDI
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You now have a VDI that is usable, but is small enough that it&amp;rsquo;s not very useful. Ubuntu Core will resize itself to fit in whichever disk you give it, so let&amp;rsquo;s make this disk a bit bigger (remember this is a VDI, so it won&amp;rsquo;t take any more space), I selected 20GB:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ VBoxManage modifyhd ubuntu-core-16-amd64.vdi --resize &lt;span style=&#34;color:#ae81ff&#34;&gt;20480&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, create a new machine within VirtualBox. When it comes to the &amp;ldquo;Hard disk&amp;rdquo; section, select &amp;ldquo;Use an existing virtual hard disk file,&amp;rdquo; and browse to the &lt;code&gt;ubuntu-core-16-amd64.vdi&lt;/code&gt; file you just created. Create the machine. I suggest you change the network interface to be bridged so you can actually SSH to the thing, and then fire it up.&lt;/p&gt;
&lt;p&gt;The rest of the directions are the same as KVM. Make sure you have an Ubuntu SSO account with associated SSH keys. As the machine comes up for the first time, eventually it&amp;rsquo;ll ask you to &amp;ldquo;Press enter to configure.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;This will immediately jump to a screen asking you to configure your network. If you&amp;rsquo;re using DHCP, just hit &amp;ldquo;Done&amp;rdquo; and move on, where it&amp;rsquo;ll ask for your email address (it wants the one associated with your Ubuntu SSO account).&lt;/p&gt;
&lt;p&gt;From here it&amp;rsquo;ll look up your account in the store, and allow you to login to the VM via the SSH keys you have in your SSO account.&lt;/p&gt;
&lt;p&gt;Enjoy your Ubuntu Core experience!&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Snapping Nextcloud: Nextcloud itself</title>
      <link>https://kyrofa.com/posts/snapping-nextcloud-nextcloud-itself/</link>
      <pubDate>Sat, 25 Jun 2016 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/snapping-nextcloud-nextcloud-itself/</guid>
      <description>In the previous post of this series, we discussed the process of and lessons learned from building Redis for the Nextcloud snap. That means we&amp;rsquo;ve covered all the major pieces except for Nextcloud itself, so for this post that&amp;rsquo;s what we&amp;rsquo;ll focus on getting that into the snap.
As I mentioned previously, Nextcloud is a PHP application. As such, it&amp;rsquo;s not difficult to get working with Apache or MySQL. However, it was written in such a way to make its deployment as easy as possible (e.</description>
      <content:encoded>&lt;p&gt;In the &lt;a href=&#34;https://kyrofa.com/posts/snapping-nextcloud-the-memcache&#34;&gt;previous post of this series&lt;/a&gt;, we discussed the process of and lessons learned from building Redis for the Nextcloud snap. That means we&amp;rsquo;ve covered all the major pieces except for Nextcloud itself, so for this post that&amp;rsquo;s what we&amp;rsquo;ll focus on getting that into the snap.&lt;/p&gt;
&lt;p&gt;As I mentioned previously, Nextcloud is a PHP application. As such, it&amp;rsquo;s not difficult to get working with Apache or MySQL. However, it was written in such a way to make its deployment as easy as possible (e.g. untar it &lt;!-- raw HTML omitted --&gt;), which led to a complication in the way I chose to package it.&lt;/p&gt;
&lt;h2 id=&#34;lesson-7-read-only-is-both-a-blessing-and-a-curse&#34;&gt;Lesson 7: Read-only is both a blessing and a curse&lt;/h2&gt;
&lt;p&gt;Snaps are squashfs images, which by definition means they&amp;rsquo;re read-only. I thought this was a pretty big plus for Nextcloud: even if it was compromised, the attacker couldn&amp;rsquo;t modify any part of the application. However, there&amp;rsquo;s a downside here, too: Nextcloud has an admin interfaces that allows a user to alter its configuration. However, that configuration isn&amp;rsquo;t stored in the database: it&amp;rsquo;s stored within a &lt;code&gt;config.php&lt;/code&gt; file which lives inside the application itself (in the &lt;code&gt;config/&lt;/code&gt; directory). Well, in a snap, that directory (and config file) is completely read-only. That means you can&amp;rsquo;t even configure email notifications, since those settings are stored in that config file.&lt;/p&gt;
&lt;p&gt;This meant I had to choose one of two paths:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Store the entire application in a readable location (i.e. &lt;code&gt;$SNAP_DATA&lt;/code&gt; somewhere)&lt;/li&gt;
&lt;li&gt;Store the config in a writable location, separate from the read-only application&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;(1) would mean duplicating the application, because during the installation process I&amp;rsquo;d have to copy the application from the snap to the writable area, which that means there are two copies of the application. Nextcloud is not small (it&amp;rsquo;s about 90MB all by itself), so that seems wasteful. It also defeats what I consider to be an advantage of the snap: the fact that it is read-only in the first place! So (2) seemed to be the obvious win from my perspective.&lt;/p&gt;
&lt;p&gt;However, while Nextcloud supports apps and data being hosted outside the main application, it does not support hosting the config outside of the main application. This meant I had to fork Nextcloud and &lt;a href=&#34;https://github.com/kyrofa/nextcloud/commit/6d700a313075b153a7d3377559b874d31f6720e2&#34;&gt;apply a patch adding that support&lt;/a&gt;, and use that fork in the snap. I tried to move that patch upstream back when it was ownCloud, but I was unfortunately unable to gain any traction. Perhaps I should try again with Nextcloud. (&lt;strong&gt;Update:&lt;/strong&gt; This was upstreamed in Nextcloud v10; the snap no longer uses a fork).&lt;/p&gt;
&lt;p&gt;Speaking of hosting data outside the main application, that led to:&lt;/p&gt;
&lt;h2 id=&#34;lesson-8-versioned-data-directories-dont-cover-all-use-cases&#34;&gt;Lesson 8: Versioned data directories don&amp;rsquo;t cover all use-cases&lt;/h2&gt;
&lt;p&gt;If you read the available documentation and take a look at the environment in which snap applications and services execute, you&amp;rsquo;ll note that there are two writable areas available to all snaps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$SNAP_DATA&lt;/code&gt;: system-wide writable area.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$SNAP_USER_DATA&lt;/code&gt;: user-specific writable area.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both of these areas are contained within versioned directories, i.e. the data contained within them are version-specific. What do I mean by this? Let me explain with an example:&lt;/p&gt;
&lt;p&gt;Say you install version 1 of snap &amp;ldquo;foo&amp;rdquo;. Say it writes files into both &lt;code&gt;$SNAP_DATA&lt;/code&gt; and &lt;code&gt;$SNAP_USER_DATA&lt;/code&gt;. Now an update to &amp;ldquo;foo&amp;rdquo; is released: version 2. As part of the upgrade process, snapd copies version 1&amp;rsquo;s data directories into version 2&amp;rsquo;s data directories, and then starts version 2 of &amp;ldquo;foo&amp;rdquo;. Version 2 fires up and is running on the data that was last touched by version 1. Maybe it runs some migrations on it.&lt;/p&gt;
&lt;p&gt;What if those migrations fail? Well, then snapd can revert (or rollback) version 2, and start running version 1 again. Since snapd made a copy of version 1&amp;rsquo;s data before the migration occurred, version 1 is fired up running on the data exactly as it left it rather than on the data where the failed migration occurred.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s lovely, but it&amp;rsquo;s problematic when it comes to applications like Nextcloud. My personal Nextcloud collection of data is several terabytes. Automatically copying that data every time an upgrade occurrs just in case Nextcloud needs to rollback is not a scalable workflow unless you have some serious hard drive space. This led to the &lt;a href=&#34;https://bugs.launchpad.net/snappy/+bug/1559248&#34;&gt;development of a new feature&lt;/a&gt; in snapd, a data directory that was common across all versions of a given snap. This feature is not yet documented because those directories are &lt;a href=&#34;https://bugs.launchpad.net/ubuntu/+source/snapd/+bug/1577549&#34;&gt;not yet represented by an environment variable&lt;/a&gt;, but the Nextcloud snap makes use of them anyway for storing its raw data. (&lt;strong&gt;Update:&lt;/strong&gt; They are now: &lt;code&gt;$SNAP_COMMON&lt;/code&gt; and &lt;code&gt;$SNAP_USER_COMMON&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;Beyond those issues, Nextcloud was pretty straight-forward to get up and running in the snap. It provides the ability to pre-configure some settings which allowed me to make the initial setup nice and painless (e.g. pre-configure the database, etc.). I wish they exposed more things that way though&amp;ndash; I&amp;rsquo;d also like to set the background jobs to using cron instead of ajax, but you can only do that after the application is installed (i.e. an admin user is created), so the snap is still using ajax. Maybe I&amp;rsquo;ll try proposing a change (&lt;strong&gt;Update:&lt;/strong&gt; the cron type is now automatically changed once the cron job runs).&lt;/p&gt;
&lt;p&gt;But all in all, I&amp;rsquo;m quite happy with the snap.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Snapping Nextcloud: The memcache</title>
      <link>https://kyrofa.com/posts/snapping-nextcloud-the-memcache/</link>
      <pubDate>Fri, 24 Jun 2016 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/snapping-nextcloud-the-memcache/</guid>
      <description>In the previous post of this series, we discussed the process of and lessons learned from building MySQL for the Nextcloud snap. The last piece of infrastructure we need to introduce is the memcache, for which we&amp;rsquo;ll use Redis. Nextcloud supports a number of memcache backends; I chose Redis because it can provide both a local cache as well as file locking, so for this post that&amp;rsquo;s what we&amp;rsquo;ll focus on getting into the snap.</description>
      <content:encoded>&lt;p&gt;In the &lt;a href=&#34;https://kyrofa.com/posts/snapping-nextcloud-mysql&#34;&gt;previous post of this series&lt;/a&gt;, we discussed the process of and lessons learned from building MySQL for the Nextcloud snap. The last piece of infrastructure we need to introduce is the memcache, for which we&amp;rsquo;ll use Redis. Nextcloud supports a number of memcache backends; I chose Redis because it can provide both a local cache as well as file locking, so for this post that&amp;rsquo;s what we&amp;rsquo;ll focus on getting into the snap.&lt;/p&gt;
&lt;p&gt;Since I was already building all the other components from source, I decided to do the same for Redis. I hit &lt;em&gt;Lesson 4&lt;/em&gt; again (&lt;code&gt;DESTIR&lt;/code&gt; is not standard) so I wrote a dead simple local plugin for it to call &lt;code&gt;make install PREFIX=&amp;lt;installdir&amp;gt;&lt;/code&gt;, but hardly anything worth mentioning. However, I encountered what was very nearly a deal-breaking problem:&lt;/p&gt;
&lt;h2 id=&#34;lesson-6-not-all-projects-support-environment-variables-in-their-configuration-files&#34;&gt;Lesson 6: Not all projects support environment variables in their configuration files&lt;/h2&gt;
&lt;p&gt;Redis is pretty neat in that it&amp;rsquo;ll happily run using a default configuration, but I didn&amp;rsquo;t like its default configuration. I didn&amp;rsquo;t want to listen on the network, I wanted to use unix sockets. I wanted to log and dump the database into &lt;code&gt;$SNAP_DATA/redis/&lt;/code&gt;. You get the idea. But Redis doesn&amp;rsquo;t evaluate environment variables in its config file, so it was throwing errors complaining about not being able to open the file &amp;ldquo;${SNAP_DATA}/redis/redis.log&amp;rdquo;, etc.&lt;/p&gt;
&lt;p&gt;So I took a careful look at its help page, and noticed this little nugget:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ ./redis-server -h
Usage: &amp;lt;snip&amp;gt;
      ./redis-server - &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;read config from stdin&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
      &amp;lt;snip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So I decided to rewrite the config file on the fly, evaluating the environment variables myself, and hand it to its stdin:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/sh
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
envsubst &amp;lt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$SNAP&lt;span style=&#34;color:#e6db74&#34;&gt;/config/redis/redis.conf&amp;#34;&lt;/span&gt; | redis-server -
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;All that was left was to update the Nextcloud configuration to use Redis as the backend for the memcache and file locking, and voila!&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://kyrofa.com/posts/snapping-nextcloud-nextcloud-itself&#34;&gt;next (and final) post in the series&lt;/a&gt; will discuss the Nextcloud web application itself.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Snapping Nextcloud: MySQL</title>
      <link>https://kyrofa.com/posts/snapping-nextcloud-mysql/</link>
      <pubDate>Thu, 23 Jun 2016 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/snapping-nextcloud-mysql/</guid>
      <description>In the previous post of this series, we discussed the process of and lessons learned from building PHP 7 for the Nextcloud snap. Nextcloud supports a number of databases, but they recommend MySQL (the v9 docs that I used when making this decision are lost to the dust of time, but MySQL is still the recommended option in v21), so for this post that&amp;rsquo;s what we&amp;rsquo;ll focus on getting into the snap.</description>
      <content:encoded>&lt;p&gt;In the &lt;a href=&#34;https://kyrofa.com/posts/snapping-nextcloud-php-7&#34;&gt;previous post of this series&lt;/a&gt;, we discussed the process of and lessons learned from building PHP 7 for the Nextcloud snap. Nextcloud supports a number of databases, but they recommend MySQL (the v9 docs that I used when making this decision are lost to the dust of time, but MySQL is still the recommended option in &lt;a href=&#34;https://docs.nextcloud.com/server/21/admin_manual/installation/system_requirements.html&#34;&gt;v21&lt;/a&gt;), so for this post that&amp;rsquo;s what we&amp;rsquo;ll focus on getting into the snap.&lt;/p&gt;
&lt;p&gt;MySQL is included in the Ubuntu archives, and honestly something like this should work fine:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;parts&lt;/span&gt;:
       &lt;span style=&#34;color:#f92672&#34;&gt;mysql&lt;/span&gt;:
           &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;nil&lt;/span&gt;
           &lt;span style=&#34;color:#f92672&#34;&gt;stage-packages&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;mysql-server]&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But it doesn&amp;rsquo;t.&lt;/p&gt;
&lt;h2 id=&#34;lesson-5-seccomp-denials-are-not-recoverable&#34;&gt;Lesson 5: seccomp denials are not recoverable&lt;/h2&gt;
&lt;p&gt;I find that a lot of the people I talk to think of snaps as containers (they often compare them to docker). Yes, snaps are self-contained. Yes, snaps are isolated. But in my opinion, no, snaps are not containers. Admittedly they utilize some of the same underlying technology, but they aren&amp;rsquo;t in a chroot of any kind, they don&amp;rsquo;t get their own network namespace, etc. They&amp;rsquo;re simply very confined applications, and calling them containers is confusing to those who are familiar with other kinds of containers. It&amp;rsquo;s not my goal to discuss the entire security story behind snaps (check the &lt;a href=&#34;https://assets.ubuntu.com/v1/66fcd858-canonical-ubuntu-core-security-2018-11-13.pdf&#34;&gt;whitepaper&lt;/a&gt; out if you&amp;rsquo;re interested), but you need to know a part of it for this lesson.&lt;/p&gt;
&lt;p&gt;One of the confinement technologies used in snaps are seccomp filters, which allows one to provide a whitelist of system calls for a given application environment. This is one of the things handled by the concept of &amp;ldquo;interfaces&amp;rdquo;. When you specify interfaces for a given application, you expand the whitelist of syscalls it&amp;rsquo;s allowed to make (among other things). When configuring the whitelist, one specifies what should happen if a disallowed syscall is made. There are several options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Kill the offending application immediately.&lt;/li&gt;
&lt;li&gt;Notify the offending application via a signal.&lt;/li&gt;
&lt;li&gt;Return an errno from the syscall.&lt;/li&gt;
&lt;li&gt;Notify a tracer of the denial.&lt;/li&gt;
&lt;li&gt;Allow the syscall.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Only two of these options were valid for the snap use-case: either kill the offending application, or return an errno from the syscall. The other options were I believe either racy or impossible to implement in a generic manner. The obvious winner there is to return an errno&amp;ndash; let the application handle its own denial, maybe it can recover. However, therein lies a seccomp filters shortcoming: if one uses the errno option, none of the denials get logged! So the security team chose the last remaining option: kill the offending application immediately.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s terribly unfriendly to the application, but at least you get a warning about it in the syslog so you know what the issue is (and &lt;code&gt;snappy-debug&lt;/code&gt; will make recommendations for you). It&amp;rsquo;s worth noting here that the security team is working with upstreams to enable logging for errno, so we hope to use that instead once it&amp;rsquo;s available. (&lt;strong&gt;Update:&lt;/strong&gt; that work has completed, and snapd now uses errno instead of killing offending applications. Yes, it makes this lesson useless, but I&amp;rsquo;m leaving it intact for historical reasons.)&lt;/p&gt;
&lt;p&gt;Anyway, MySQL attempts to control the priority of its page cleaner threads with a &lt;code&gt;setpriority()&lt;/code&gt; syscall, which is not contained within any of snapd&amp;rsquo;s interfaces. MySQL makes this call, and rightly handles its failure, but snapd doesn&amp;rsquo;t give it a chance to handle the failure: it kills it immediately. The only way around this was to fork MySQL, &lt;a href=&#34;https://github.com/kyrofa/mysql-server/commit/dd0e4e497794da2650536097655f4bf732b261a9&#34;&gt;apply a patch to make that syscall optional&lt;/a&gt;, and build the MySQL part from source. I&amp;rsquo;m trying to move that patch upstream, but the MySQL folks seem reluctant to accept it since they don&amp;rsquo;t understand why snapd doesn&amp;rsquo;t use errno instead of kill. I can&amp;rsquo;t really blame them, so I&amp;rsquo;ll probably be maintaining this fork until snapd uses errno.&lt;/p&gt;
&lt;p&gt;Beyond that issue, MySQL was pretty easy to get working. It runs based on config files which can contain environment variables, so I just ship custom versions in the snap.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://kyrofa.com/posts/snapping-nextcloud-the-memcache&#34;&gt;next post in the series&lt;/a&gt; will discuss the memcache.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Snapping Nextcloud: PHP 7</title>
      <link>https://kyrofa.com/posts/snapping-nextcloud-php-7/</link>
      <pubDate>Wed, 22 Jun 2016 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/snapping-nextcloud-php-7/</guid>
      <description>In the previous post of this series, we discussed the process of and lessons learned from building Apache for the Nextcloud snap. Nextcloud is a web application written in PHP, so for this post we&amp;rsquo;ll focus on getting PHP into the snap.
PHP 7, like Apache, is included in the Ubuntu archives. However, I was planning on using the PHP Apache module, and since I was building Apache from source, I figured there&amp;rsquo;d be a lot of hoops for me to jump through if I tried to shoehorn the .</description>
      <content:encoded>&lt;p&gt;In the &lt;a href=&#34;https://kyrofa.com/posts/snapping-nextcloud-the-web-server&#34;&gt;previous post of this series&lt;/a&gt;, we discussed the process of and lessons learned from building Apache for the Nextcloud snap. Nextcloud is a web application written in PHP, so for this post we&amp;rsquo;ll focus on getting PHP into the snap.&lt;/p&gt;
&lt;p&gt;PHP 7, like Apache, is included in the Ubuntu archives. However, I was planning on using the PHP Apache module, and since I was building Apache from source, I figured there&amp;rsquo;d be a lot of hoops for me to jump through if I tried to shoehorn the .deb into working for me. Since building Apache modules is really pretty straight-forward, I just went straight for building the PHP Apache module from source, as a new part in the &lt;code&gt;snapcraft.yaml&lt;/code&gt;. Of course, it uses the various Apache headers and tools for building modules, which is another part in the YAML, and that relationship is specified by using the &lt;code&gt;after&lt;/code&gt; keyword in snapcraft, something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;parts&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;apache&lt;/span&gt;:
       &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;apache&lt;/span&gt;
       &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;php&lt;/span&gt;:
       &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;autotools&lt;/span&gt;
       &lt;span style=&#34;color:#f92672&#34;&gt;after&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;apache]&lt;/span&gt;
       &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Using the &lt;code&gt;after&lt;/code&gt; keyword like this tells snapcraft that the &lt;code&gt;php&lt;/code&gt; part depends upon the &lt;code&gt;apache&lt;/code&gt; part, and &lt;code&gt;apache&lt;/code&gt; should be staged before &lt;code&gt;php&lt;/code&gt; is built. However, going down this path quickly led me to:&lt;/p&gt;
&lt;h2 id=&#34;lesson-3-project-build-systems-arent-really-prepared-for-snapcraft&#34;&gt;Lesson 3: Project build systems aren&amp;rsquo;t really prepared for snapcraft&lt;/h2&gt;
&lt;p&gt;This is pretty similar to Lesson 2 in the previous post in that the problem is the same, but the effect is different. As I mentioned in the previous post, Apache writes its prefix everywhere, including its tools for building modules (e.g. &lt;code&gt;apxs&lt;/code&gt;). This has the effect that modules built using &lt;code&gt;apxs&lt;/code&gt; get installed into &lt;em&gt;that same prefix&lt;/em&gt;. There doesn&amp;rsquo;t seem to be any way to redirect it elsewhere.&lt;/p&gt;
&lt;p&gt;This is a problem because snapcraft was designed for parts to be completely independent. They may depend upon another part if it&amp;rsquo;s already in the staging area, but they cannot alter each others files or directories. We could work around this fact by rewriting the prefix in &lt;code&gt;apxs&lt;/code&gt; to point to the staging area, but then we hit another problem: snapcraft was also designed for each part to go through an explicit lifecycle: &lt;code&gt;pull&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;stage&lt;/code&gt;, and &lt;code&gt;prime&lt;/code&gt; (followed by the final &lt;code&gt;snap&lt;/code&gt;). Having parts install directly into the staging area means they essentially skip the &lt;code&gt;build&lt;/code&gt; step, which throws snapcraft&amp;rsquo;s state tracking off and makes it unhappy. Future snapcraft developments may make this easier, but we&amp;rsquo;re not there yet.&lt;/p&gt;
&lt;p&gt;The solution to this problem without fighting either snapcraft or &lt;code&gt;apxs&lt;/code&gt; too much is to simply build Apache modules as part of the &lt;a href=&#34;https://github.com/nextcloud/nextcloud-snap/blob/629aa1045c815ec4add8198c167ec2409ff40465/parts/plugins/x-apache.py&#34;&gt;Apache plugin&lt;/a&gt;, thus keeping it within the same part (you&amp;rsquo;ll notice the &lt;code&gt;third-party-modules&lt;/code&gt; config option within the plugin). When developing that code, I quickly ran into another issue:&lt;/p&gt;
&lt;h2 id=&#34;lesson-4-destdir-is-not-standard&#34;&gt;Lesson 4: DESTDIR is not standard&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve mentioned the concept of relocatable builds a few times: snapcraft configures projects to install into location X, installs into location X, and snaps run from location Y. That&amp;rsquo;s actually not quite true. It&amp;rsquo;s actually more like: configure to install into location X, install into location Y, and run from location Z. What do I mean by this? Well, snapcraft builds each part within a &lt;code&gt;parts/&amp;lt;partname&amp;gt;/build/&lt;/code&gt; subdirectory, and installs them into &lt;code&gt;parts/&amp;lt;partname&amp;gt;/install/&lt;/code&gt;. It does this even though the project might be configured to install into &lt;code&gt;/usr/bin/&lt;/code&gt;, and it uses &lt;code&gt;DESTDIR&lt;/code&gt; to do that.&lt;/p&gt;
&lt;p&gt;For example, if you look at snapcraft&amp;rsquo;s &lt;code&gt;make&lt;/code&gt; plugin, you&amp;rsquo;ll see that it runs &lt;code&gt;make&lt;/code&gt; followed by &lt;code&gt;make install DESTDIR=&amp;lt;installdir&amp;gt;&lt;/code&gt;. Many build systems respect this as a de-facto standard, and essentially install their artifacts into &lt;code&gt;DESTDIR/&amp;lt;prefix&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;However, PHP does not. The PHP build system completely ignores &lt;code&gt;DESTDIR&lt;/code&gt; and tries to install to the configured prefix, in the root filesystem somewhere, and dies because it doesn&amp;rsquo;t have permission to do so. It wouldn&amp;rsquo;t get into the snap that way anyway! This actually led to the creation of a new snapcraft feature for the autotools plugin: the &lt;code&gt;install-via&lt;/code&gt; option. Using that option it&amp;rsquo;s possible to make the autotools plugin not use &lt;code&gt;DESTIR&lt;/code&gt;, and instead use a series of incantations like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ ./configure --prefix&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;parts/&amp;lt;partname&amp;gt;/install
$ make
$ make install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note the lack of &lt;code&gt;DESTDIR&lt;/code&gt; there; it&amp;rsquo;s just installing with the prefix instead. This allowed PHP to install correctly into the snap.&lt;/p&gt;
&lt;p&gt;Note that I&amp;rsquo;m actually currently in the process of moving to PHP FPM instead of the Apache module, for various reasons, but these lessons still apply.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://kyrofa.com/posts/snapping-nextcloud-mysql&#34;&gt;next post in the series&lt;/a&gt; will discuss MySQL.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Snapping Nextcloud: The web server</title>
      <link>https://kyrofa.com/posts/snapping-nextcloud-the-web-server/</link>
      <pubDate>Tue, 21 Jun 2016 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/snapping-nextcloud-the-web-server/</guid>
      <description>The backbone of any web application is of course the web server, so that&amp;rsquo;s where I started when snapping Nextcloud. I went with Apache as opposed to Nginx for two reasons: 1) Apache is recommended by Nextcloud (the v9 docs that I used when making this decision are lost to the dust of time, but Apache is still the recommended option in v21), and 2) I&amp;rsquo;m much more familiar with Apache than Nginx.</description>
      <content:encoded>&lt;p&gt;The backbone of any web application is of course the web server, so that&amp;rsquo;s where I started when snapping Nextcloud. I went with Apache as opposed to Nginx for two reasons: 1) Apache is recommended by Nextcloud (the v9 docs that I used when making this decision are lost to the dust of time, but Apache is still the recommended option in &lt;a href=&#34;https://docs.nextcloud.com/server/21/admin_manual/installation/system_requirements.html&#34;&gt;v21&lt;/a&gt;), and 2) I&amp;rsquo;m much more familiar with Apache than Nginx.&lt;/p&gt;
&lt;p&gt;Apache is in the Ubuntu archives, so it seemed that a reasonable starting point was to simply include it via stage-packages, something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;parts&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;apache&lt;/span&gt;:
       &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;nil&lt;/span&gt;
       &lt;span style=&#34;color:#f92672&#34;&gt;stage-packages&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;apache2]&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I quickly hit pain going this route, which leads me to:&lt;/p&gt;
&lt;h2 id=&#34;lesson-1-debian-packages-only-get-you-so-far&#34;&gt;Lesson 1: Debian packages only get you so far&lt;/h2&gt;
&lt;p&gt;Debian uses a different layout for Apache than the default. In particular, the method of determining which configuration, module, and/or site is enabled is not done directly in the main configuration file but via symbolic links. That makes it pretty easy to use, which I assume is the reason it was done, but the problem is this: those symlinks are setup in the Debian package&amp;rsquo;s &lt;code&gt;postinst&lt;/code&gt;, not distributed in the package itself. In simplistic terms, that essentially means the steps taken to install the package are the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Unpack the package, placing things where they need to go.&lt;/li&gt;
&lt;li&gt;Run the &lt;code&gt;postinst&lt;/code&gt; script, which calls familiar commands like &lt;code&gt;a2enmod&lt;/code&gt; etc. to setup the default modules and so on.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In snapcraft, the &lt;code&gt;stage-packages&lt;/code&gt; are only unpacked&amp;ndash; the &lt;code&gt;postinst&lt;/code&gt; script never gets run. I&amp;rsquo;m sure there are various good reasons for this, but the obvious one is this: the &lt;code&gt;stage-packages&lt;/code&gt; are unpacked into a build directory, not onto the system. Many &lt;code&gt;postinst&lt;/code&gt; scripts have hard-coded paths which wouldn&amp;rsquo;t work in this case. Regardless, the side effect is that, if I wanted to make use of Ubuntu&amp;rsquo;s Apache package, I&amp;rsquo;d need to recreate all those symlinks myself. That, along with a few other reasons, led me toward compiling Apache from source instead. Where I immediately learned:&lt;/p&gt;
&lt;h2 id=&#34;lesson-2-project-build-systems-arent-really-prepared-for-snappy&#34;&gt;Lesson 2: Project build systems aren&amp;rsquo;t really prepared for snappy&lt;/h2&gt;
&lt;p&gt;The design of snaps pushes the envelope in a few places, so it&amp;rsquo;s understandable to see some incompatibilities. The one I&amp;rsquo;m talking about here is the concept of a relocatable build. Most projects typically use their build systems (autotools, cmake, etc.) to configure a project to install into location X, install the project from location X, and the project runs from location X. This works fine for Debian packaging, where it can unpack files into, say, &lt;code&gt;/usr/bin/&lt;/code&gt;. However, snaps are confined to their own area in &lt;code&gt;/snap/&amp;lt;snapname&amp;gt;/&amp;lt;version&amp;gt;/&lt;/code&gt;, so a build system that might be configured for installing into &lt;code&gt;/usr/bin/&lt;/code&gt; will actually end up running out of &lt;code&gt;/snap/&amp;lt;snapname&amp;gt;/&amp;lt;version&amp;gt;/usr/bin/&lt;/code&gt; (for example). It means the project is configured to install into location X, is installed into location X, and runs from location Y.&lt;/p&gt;
&lt;p&gt;This isn&amp;rsquo;t the fault of autotools or cmake, necessarily. It&amp;rsquo;s perfectly possible to have a relocatable project that uses those tools, but projects historically haven&amp;rsquo;t needed to bother with such things. A great example of this is Apache. It&amp;rsquo;s an autotools project, and when you give it a prefix while configuring it, it writes that prefix everywhere. Config files, hard-coded in scripts, you name it. These paths have to be valid at build- and run-time. That&amp;rsquo;s great if you&amp;rsquo;re installing and running out of the same place&amp;hellip; but with snaps, you aren&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;Thankfully, Apache is one of the most configurable projects I&amp;rsquo;ve ever seen. Everything can be set via a config file and/or command-line switches. I essentially had to build Apache in two phases: install Apache and its modules, then clean it up. I did this with a &lt;a href=&#34;https://github.com/nextcloud/nextcloud-snap/blob/629aa1045c815ec4add8198c167ec2409ff40465/parts/plugins/x-apache.py&#34;&gt;local snapcraft plugin&lt;/a&gt;, which gave me full control over the build process. I installed Apache and any extra modules that were requested, and then did one massive search/replace to enable it to be run from a snap. I could probably simplify this further by shipping a custom Apache configuration in another snapcraft part, but this works for now.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://kyrofa.com/posts/snapping-nextcloud-php-7&#34;&gt;next post in this series&lt;/a&gt; will discuss PHP.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Installing Nextcloud can be a snap</title>
      <link>https://kyrofa.com/posts/installing-nextcloud-can-be-a-snap/</link>
      <pubDate>Mon, 20 Jun 2016 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/installing-nextcloud-can-be-a-snap/</guid>
      <description>I&amp;rsquo;ve been an ownCloud user for years, and I recently moved to using Nextcloud, which is based upon ownCloud. Both my wife and I use it to sync our calendars and contacts across all our devices, and also use it to sync our documents, music, pictures, and videos. It caters to my paranoid nature&amp;ndash; the idea that all that information is being saved on my own in-house server instead of someone else&amp;rsquo;s greatly pleases me.</description>
      <content:encoded>&lt;p&gt;I&amp;rsquo;ve been an &lt;a href=&#34;https://owncloud.org/&#34;&gt;ownCloud&lt;/a&gt; user for years, and I recently moved to using &lt;a href=&#34;https://nextcloud.com/&#34;&gt;Nextcloud&lt;/a&gt;, which is based upon ownCloud. Both my wife and I use it to sync our calendars and contacts across all our devices, and also use it to sync our documents, music, pictures, and videos. It caters to my paranoid nature&amp;ndash; the idea that all that information is being saved on my own in-house server instead of someone else&amp;rsquo;s greatly pleases me.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been on the snappy team for a little while now, and I still find one of the best ways to gain understanding about the system as well as prove out new functionality and find bugs is to simply create snaps. Snaps provide a particularly easy way to install software, and Nextcloud is a particularly complex system that requires a bit of work and knowledge to setup. Wouldn&amp;rsquo;t it be cool if you could install Nextcloud and it came all pre-configured and ready to go? Check this out:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snap install nextcloud
120.87 MB / 120.87 MB &lt;span style=&#34;color:#f92672&#34;&gt;[==================]&lt;/span&gt; 100.00 % 2.78 MB/s 
Name      Version     Rev Developer Notes
nextcloud 9.0.50snap2 &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;   canonical -
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Visit localhost (or the device&amp;rsquo;s external IP) in your browser and you&amp;rsquo;ll see:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/nextcloud-login.jpg&#34; alt=&#34;nextcloud login&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;Create an admin account by entering a desired username and password there, and you have a working Nextcloud installation. It&amp;rsquo;s using Apache, PHP 7, MySQL for the database, and redis for the cache, all installed and setup with a few keystrokes. It even includes mDNS for network discovery in case you installed it on a headless box. It&amp;rsquo;s also completely confined, so if (heaven forbid) Nextcloud was compromised, the attacker wouldn&amp;rsquo;t be able to walk all over your system.&lt;/p&gt;
&lt;p&gt;There are, of course, limitations.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The target audience: this snap is really for those users who just want to get Nextcloud up and running with recommended settings. The side effect of getting all this preconfiguration and easy upgrades is that it&amp;rsquo;s not very customizable. If you&amp;rsquo;re wanting to run Nextcloud behind a load balancer connected to a MySQL cluster, you&amp;rsquo;re probably better off setting it up yourself.&lt;/li&gt;
&lt;li&gt;HTTPS is not yet supported. I don&amp;rsquo;t recommend the use of this snap on production systems until it is. I&amp;rsquo;m working on on integrating Let&amp;rsquo;s Encrypt into the snap, but I want to make it as user-friendly as possible, which will take a little time. (&lt;strong&gt;Update:&lt;/strong&gt; HTTPS support was added in 9.0.53snap2, released on 08/22/16.)&lt;/li&gt;
&lt;li&gt;Overall still a work in progress, and probably has unknown rough edges.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;rsquo;m pleased with how far it&amp;rsquo;s come, but getting there wasn&amp;rsquo;t easy. In the following series of blog posts, I&amp;rsquo;d like to share some of the stumbling blocks I hit along the way (as well as how I solved them). I hope that doing so will help you snap up your complex system.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://kyrofa.com/posts/snapping-nextcloud-the-web-server&#34;&gt;next post in this series&lt;/a&gt; will discuss the web server.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Building your snap on-device? There&#39;s a better way</title>
      <link>https://kyrofa.com/posts/building-your-snap-on-device-there-s-a-better-way/</link>
      <pubDate>Mon, 09 May 2016 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/building-your-snap-on-device-there-s-a-better-way/</guid>
      <description>I&amp;rsquo;m currently the maintainer of the ownCloud snap. One of the primary target devices for this snap is a Raspberry Pi 2, which is of course armhf. Snapcraft solves a lot of problems when building snaps, but one issue it doesn&amp;rsquo;t solve is cross-building (i.e. building on one architecture while targeting another). Back in Snappy Ubuntu Core 15.04, every ownCloud release I made for armhf was built on the Raspberry Pi 2.</description>
      <content:encoded>&lt;p&gt;I&amp;rsquo;m currently the maintainer of the &lt;a href=&#34;https://github.com/kyrofa/owncloud-snap&#34;&gt;ownCloud snap&lt;/a&gt;. One of the primary target devices for this snap is a Raspberry Pi 2, which is of course armhf. Snapcraft solves a lot of problems when building snaps, but one issue it doesn&amp;rsquo;t solve is cross-building (i.e. building on one architecture while targeting another). Back in Snappy Ubuntu Core 15.04, every ownCloud release I made for armhf was built on the Raspberry Pi 2. What a painful experience that was&amp;ndash; it needed more RAM than the rpi2 had (so I needed to enable swap), and it took literally all day to build.&lt;/p&gt;
&lt;p&gt;Nowadays there&amp;rsquo;s a much, much better way. Snapcraft still doesn&amp;rsquo;t fully support cross-building, but the &lt;a href=&#34;https://launchpad.net/&#34;&gt;Launchpad&lt;/a&gt; team recently introduced snap builders for several different architectures that run on very high-end hardware&amp;ndash; available for free, for everyone! The only downside is that they aren&amp;rsquo;t very well documented. Maybe I can help with that.&lt;/p&gt;
&lt;h2 id=&#34;what-are-you-talking-about&#34;&gt;What are you talking about?&lt;/h2&gt;
&lt;p&gt;The Launchpad snap builders are server instances spun up on demand with the sole purpose of building snaps using Snapcraft in a clean environment. They have servers for several different architectures&amp;ndash; at the time of this writing, amd64, x86, arm64, armhf, and ppc64. I strongly encourage you to use them.&lt;/p&gt;
&lt;h2 id=&#34;how-do-i-use-them&#34;&gt;How do I use them?&lt;/h2&gt;
&lt;p&gt;First of all, you need a Launchpad account. Once you have that, you just need your code in a branch on Launchpad. You can do that by creating a project and associating you code with it, or by pushing to the &lt;a href=&#34;https://help.launchpad.net/Code/PersonalBranches&#34;&gt;+junk pseudo project&lt;/a&gt;. I&amp;rsquo;ll walk you through using a project, but the +junk project is really no different, it just cuts down a step. Launchpad supports both bzr and git&amp;ndash; I&amp;rsquo;ll use git in this example.&lt;/p&gt;
&lt;h3 id=&#34;step-1-register-a-project&#34;&gt;Step 1: Register a project&lt;/h3&gt;
&lt;p&gt;Go ahead and &lt;a href=&#34;https://launchpad.net/projects/+new&#34;&gt;create a project&lt;/a&gt; with which the code will be associated. I called mine &amp;ldquo;&lt;a href=&#34;https://launchpad.net/owncloud-snap&#34;&gt;owncloud-snap&lt;/a&gt;&amp;rdquo;.&lt;/p&gt;
&lt;h3 id=&#34;step-2-push-the-code&#34;&gt;Step 2: Push the code&lt;/h3&gt;
&lt;p&gt;Since the ownCloud snap is hosted in GitHub, I just needed to add Launchpad as a remote and push up my branch:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ git remote add lp git+ssh://kyrofa@git.launchpad.net/owncloud-snap
$ git push lp master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you should see your new branch in the Code tab of your project (e.g. &lt;a href=&#34;https://code.launchpad.net/owncloud-snap&#34;&gt;here&amp;rsquo;s ownCloud&amp;rsquo;s&lt;/a&gt;).&lt;/p&gt;
&lt;h3 id=&#34;step-3-create-a-snap-associated-with-the-branch-you-just-pushed&#34;&gt;Step 3: Create a snap associated with the branch you just pushed&lt;/h3&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/launchpad-branch.jpg&#34; alt=&#34;launchpad branch&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;Select the new branch in the Code tab. One of the options you&amp;rsquo;ll see is &amp;ldquo;Create snap package.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/launchpad-create-snap.jpg&#34; alt=&#34;launchpad create snap&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;Select that, and you&amp;rsquo;ll be able to name your snap, and specify what distribution to use.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/launchpad-snap-options.jpg&#34; alt=&#34;launchpad snap options&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;There are many options here, but you either want to select &lt;code&gt;Vivid&lt;/code&gt; (if targeting Snappy Ubuntu Core 15.04) or &lt;code&gt;Xenial&lt;/code&gt; (if targeting Snappy Ubuntu Core 16)&amp;ndash; ignore the rest. You can even opt to automatically upload completed snaps to the store here if you like. When you&amp;rsquo;re happy the settings, select &amp;ldquo;Create snap package.&amp;rdquo;&lt;/p&gt;
&lt;h3 id=&#34;step-4-make-sure-its-using-the-architectures-you-want&#34;&gt;Step 4: Make sure it&amp;rsquo;s using the architectures you want.&lt;/h3&gt;
&lt;p&gt;After completing step 3 you should be on the &amp;ldquo;Snap package information&amp;rdquo; page for the snap you just created. By default (at least at the time of this writing), snaps are only built for amd64 and x86.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/launchpad-edit-snap.jpg&#34; alt=&#34;launchpad edit snap&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;On the right, select &amp;ldquo;Edit snap package&amp;rdquo; and you&amp;rsquo;ll be able to select other architectures for which the snap will build. The store will accept any of these architectures, but you&amp;rsquo;ll see I&amp;rsquo;m only building for the four main ones.&lt;/p&gt;
&lt;h3 id=&#34;step-5-build-it&#34;&gt;Step 5: Build it!&lt;/h3&gt;
&lt;p&gt;Now that it&amp;rsquo;s configured for the architectures you want, get back on the &amp;ldquo;Snap package information&amp;rdquo; page and select &amp;ldquo;Request builds.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/launchpad-request-snap-builds.jpg&#34; alt=&#34;launchpad request snap builds&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;Select the architectures for which you&amp;rsquo;d like to build (note the options here are limited to what you selected in step 4), as well as the pocket to use for building the snap. Pockets are essentially &amp;ldquo;sections&amp;rdquo; of the archive. The &lt;code&gt;Release&lt;/code&gt; pocket reflects things as they were when that version of Ubuntu was released. &lt;code&gt;Updates&lt;/code&gt; (the default) reflects all the updates that the version of Ubuntu has received since release. &lt;code&gt;Security&lt;/code&gt; reflects security updates that the version has received since release&amp;hellip; you get the idea. I always use &lt;code&gt;Updates&lt;/code&gt; unless there&amp;rsquo;s a good reason not to, particularly because that&amp;rsquo;s where the Snapcraft updates land. Anyway, once you select your options, go ahead and hit &amp;ldquo;Request builds.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Step 6: Hang out for a bit. But definitely not a day.&lt;/p&gt;
&lt;p&gt;Sit back and relax while Launchpad spins up some instances and runs Snapcraft for you! For the ownCloud snap, which as I said took an entire day to build on the rpi2, Launchpad cranked out a snap for amd64, x86, arm64 and armhf in about 40 minutes. Total. Oh yes.&lt;/p&gt;
&lt;h3 id=&#34;step-7-grab-the-built-snap-and-use-it&#34;&gt;Step 7: Grab the built snap and use it.&lt;/h3&gt;
&lt;p&gt;Once a build has completed, it&amp;rsquo;ll give you a link to the built snap.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/launchpad-snap-download.jpg&#34; alt=&#34;launchpad download snap&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;You can download it from there and sideload it right away, or upload it to the store and publish it (assuming you didn&amp;rsquo;t request Launchpad to do that for you)!&lt;/p&gt;
&lt;h2 id=&#34;limitations&#34;&gt;Limitations&lt;/h2&gt;
&lt;p&gt;Now, I love the Launchpad builders, but there are a few limitations of which you should be aware.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Internet access is only available in the pull step. If your build step for instance tries to hit the internet, it&amp;rsquo;ll fail. Refactor your snapcraft.yaml if necessary. (&lt;strong&gt;Update:&lt;/strong&gt; This is no longer true, you should be able to hit the internet in any step now).&lt;/li&gt;
&lt;li&gt;The builders are still beta. Sometimes proxy tokens time out, or other infrastructure-related issues occur. You&amp;rsquo;ll get an email if the builds fail with a link to the build log&amp;ndash; take a look, and if it looks infrastructure-related, just request a new build.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you run into any other issues, or have questions, hop in the #launchpad channel in Freenode and ask away!&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>That&#39;s one Snappy MOOS</title>
      <link>https://kyrofa.com/posts/that-s-one-snappy-moos/</link>
      <pubDate>Wed, 10 Feb 2016 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/that-s-one-snappy-moos/</guid>
      <description>So you&amp;rsquo;re a roboticist, looking at Ubuntu Core and Snappy, trying to decide if they&amp;rsquo;re a good fit for your project. You come across some ROS documentation and realize that the ROS support is first-class, but you&amp;rsquo;re not using ROS. No, you&amp;rsquo;re using the Mission Oriented Operating Suite (MOOS). Why is there no documentation on using MOOS? Because it&amp;rsquo;s too easy to need a document, that&amp;rsquo;s why (update: this remains true, but we wrote a quick one anyway).</description>
      <content:encoded>&lt;p&gt;So you&amp;rsquo;re a roboticist, looking at Ubuntu Core and Snappy, trying to decide if they&amp;rsquo;re a good fit for your project. You come across some &lt;a href=&#34;https://snapcraft.io/#ros&#34;&gt;ROS documentation&lt;/a&gt; and realize that the ROS support is first-class, but you&amp;rsquo;re not using ROS. No, you&amp;rsquo;re using the Mission Oriented Operating Suite (MOOS). Why is there no documentation on using MOOS? Because it&amp;rsquo;s too easy to need a document, that&amp;rsquo;s why (&lt;strong&gt;update:&lt;/strong&gt; this remains true, but we wrote a &lt;a href=&#34;https://snapcraft.io/#moos&#34;&gt;quick one&lt;/a&gt; anyway).&lt;/p&gt;
&lt;p&gt;Note that there are two commonly-used projects here, MOOS, and MOOS IvP. The latter uses the former, and adds some more modules on top. It also has a more complicated build system when it comes to packaging, so we&amp;rsquo;re only going to talk about MOOS here (I&amp;rsquo;ll make another post regarding MOOS IvP).&lt;/p&gt;
&lt;h2 id=&#34;typical-moos-build-process&#34;&gt;Typical MOOS build process&lt;/h2&gt;
&lt;p&gt;MOOS is just good-old CMake, so the typical build process looks pretty, well, typical. First we grab and extract the latest release:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ wget https://github.com/themoos/core-moos/archive/10.0.2.a-release.tar.gz
$ tar xzf 10.0.2.a-release.tar.gz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we configure and build it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ mkdir build
$ cd build/
$ cmake ../core-moos-10.0.2.a-release/
$ make
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are even sensible install rules in there if you want to run &lt;code&gt;make install&lt;/code&gt;. Creating a new module to make use of it is also easy&amp;ndash; just refer to &lt;a href=&#34;https://github.com/themoos/examples-docs-moos/tree/master/docs/examples/AppExamples/EX1010&#34;&gt;this example&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;packaging-moos-as-a-snap&#34;&gt;Packaging MOOS as a .snap&lt;/h2&gt;
&lt;p&gt;Snaps are built using a tool called Snapcraft. For this example we&amp;rsquo;re going to target Ubuntu Core 15.04, which requires Snapcraft v1 (the latest release in that line as of this writing is v1.0.2). For more information on installing/getting started with Snapcraft, head &lt;a href=&#34;https://snapcraft.io/first-snap&#34;&gt;this direction&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s get started. First of all, we&amp;rsquo;re going to borrow &lt;a href=&#34;https://github.com/themoos/examples-docs-moos/tree/master/docs/examples/AppExamples/EX1010&#34;&gt;this example&lt;/a&gt; from MOOS, with one modification to the &lt;code&gt;CMakeLists.txt&lt;/code&gt;: an install rule. Just put it at the bottom, like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-cmake&#34; data-lang=&#34;cmake&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# ...
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;INSTALL(&lt;span style=&#34;color:#e6db74&#34;&gt;TARGETS&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;${&lt;/span&gt;EXECNAME&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
 &lt;span style=&#34;color:#e6db74&#34;&gt;RUNTIME&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;DESTINATION&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;bin&lt;/span&gt;
)&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If this isn&amp;rsquo;t clear, the finished code for this post has been pushed &lt;a href=&#34;https://github.com/kyrofa/moos-example-snap&#34;&gt;here&lt;/a&gt;. We need the install rule, or Snapcraft won&amp;rsquo;t know which files to place where when creating the final .snap.&lt;/p&gt;
&lt;p&gt;Now we&amp;rsquo;re going to create the following directory structure:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;moos-example/
  snapcraft.yaml
  icon.svg
  src/
     CMakeLists.txt
     ex1010.cpp
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;snapcraft.yaml&lt;/code&gt; file is the recipe used by Snapcraft to &amp;ldquo;craft&amp;rdquo; the snap. Open it up in your favorite editor.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s begin by providing some general metadata about the .snap, so add something like the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;moos-example&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;vendor&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;me &amp;lt;me@me.com&amp;gt;&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;summary&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;MOOS Example&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;description&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Simple ping/pong example for MOOS&lt;/span&gt;
&lt;span style=&#34;color:#f92672&#34;&gt;icon&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;icon.svg&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In Snapcraft v1 that icon is required, so if you don&amp;rsquo;t have one run this command to make one:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;svg /&amp;gt;&amp;#34;&lt;/span&gt; &amp;gt; icon.svg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Back in the &lt;code&gt;snapcraft.yaml&lt;/code&gt;, we need to tell Snapcraft about the parts making up our .snap. In our case, there are two parts: the MOOS core, and our ping/pong example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;parts&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;core-moos&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cmake&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;source&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;https://github.com/themoos/core-moos/archive/10.0.2.a-release.tar.gz&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;configflags&lt;/span&gt;:
     - -&lt;span style=&#34;color:#ae81ff&#34;&gt;DCMAKE_INSTALL_PREFIX=/&lt;/span&gt;

 &lt;span style=&#34;color:#f92672&#34;&gt;moos-example&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;plugin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cmake&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;source&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;src/&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;after&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;core-moos]&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# Build this part after MOOS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;snapcraft.yaml&lt;/code&gt; we have so far will result in a valid .snap, but it doesn&amp;rsquo;t export any behavior. Let&amp;rsquo;s make &lt;code&gt;MOOSDB&lt;/code&gt; run as a service, at boot:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;services&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;MOOSDB&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;start&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;MOOSDB&lt;/span&gt;
   &lt;span style=&#34;color:#f92672&#34;&gt;description&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;MOOS router&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s also add a few binaries to use our ping/pong example (binaries don&amp;rsquo;t run at boot, they&amp;rsquo;re run by the logged-in user):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;binaries&lt;/span&gt;:
 &lt;span style=&#34;color:#f92672&#34;&gt;ping&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;exec&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ex1010 --ping&lt;/span&gt;
 &lt;span style=&#34;color:#f92672&#34;&gt;pong&lt;/span&gt;:
   &lt;span style=&#34;color:#f92672&#34;&gt;exec&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ex1010&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So that adds two binaries: &lt;code&gt;ping&lt;/code&gt; and &lt;code&gt;pong&lt;/code&gt;. Their functionalities should be obvious.&lt;/p&gt;
&lt;p&gt;Believe it or not, we&amp;rsquo;re done here. Let&amp;rsquo;s save and close &lt;code&gt;snapcraft.yaml&lt;/code&gt;, and create the snap:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd moos-example/
$ snapcraft
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Wait for a minute while MOOS is fetched and built, then the local code is built, and finally while the .snap is being created. Once it&amp;rsquo;s finished, take it for a spin!&lt;/p&gt;
&lt;h2 id=&#34;testing-the-resulting-snap&#34;&gt;Testing the resulting .snap&lt;/h2&gt;
&lt;p&gt;Note that you need an Ubuntu Core device with the same architecture as the snap you just created (it can be a real device, or virtualized). Once you have it up and running, you can scp your snap to it and install it by running this command on the device:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snappy install /path/to/my.snap --allow-unauthenticated
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, on the device, you can see &lt;code&gt;MOOSDB&lt;/code&gt; running:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo snappy service status moos-example
Snap           Service  State
moos-example   MOOSDB   enabled; loaded; active &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;running&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So now we can run our ping/pong example. In one terminal (logged into the device), run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ moos-example.pong
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In another terminal (also logged into the device), run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ moos-example.ping
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And you&amp;rsquo;ll see them communicating, just as you&amp;rsquo;d expect.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;&amp;ldquo;So what&amp;rsquo;s the big deal? What did I just accomplish?&amp;rdquo; I hear you asking. You actually accomplished a number of things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You now have an entire MOOS system prepared for deployment/sharing. All dependencies are included in the .snap&amp;ndash; it will run on any Ubuntu Core 15.04 installation.&lt;/li&gt;
&lt;li&gt;You&amp;rsquo;ve configured exactly what should be run on boot for the robotic system, no need to fiddle with config files.&lt;/li&gt;
&lt;li&gt;By using Ubuntu Core, you now have a transactional deployment/update strategy for free. You can even get automatic updates on your devices if they connect to the internet and you upload your .snap to the store.&lt;/li&gt;
&lt;li&gt;Perhaps most importantly, your MOOS system is running completely confined. Even if it has an exploitable vulnerability, the confinement model of Ubuntu Core means an attacker&amp;rsquo;s potential damage is very limited.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If these things are important to you, I definitely suggest you spend some time experimenting with Ubuntu Core, Snappy, and Snapcraft. For your reference, the code used in this post is in &lt;a href=&#34;https://github.com/kyrofa/moos-example-snap&#34;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Seccomp filters on the Ubuntu Phone</title>
      <link>https://kyrofa.com/posts/seccomp-filters-on-the-ubuntu-phone/</link>
      <pubDate>Thu, 07 Jan 2016 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/seccomp-filters-on-the-ubuntu-phone/</guid>
      <description>The officially-supported Ubuntu Phones run pretty old kernels. The Nexus 4 and BQ phones all run v3.4, and the Meizu MX4 runs v3.10. Device kernels typically lag a bit behind since so much effort goes into making them compatible with the hardware, but this posed a bit of a complication since the Click confinement model utilizes features of AppArmor v3, which isn&amp;rsquo;t even released yet. However, we like to push the envelope here at Canonical, so we backported it into our phone kernels anyway.</description>
      <content:encoded>&lt;p&gt;The officially-supported Ubuntu Phones run pretty old kernels. The Nexus 4 and BQ phones all run v3.4, and the Meizu MX4 runs v3.10. Device kernels typically lag a bit behind since so much effort goes into making them compatible with the hardware, but this posed a bit of a complication since the Click confinement model utilizes features of AppArmor v3, which isn&amp;rsquo;t even released yet. However, we like to push the envelope here at Canonical, so we backported it into our phone kernels anyway. Unfortunately, Snappy is another story entirely.&lt;/p&gt;
&lt;p&gt;The Snappy confinement model utilizes not only AppArmor, but seccomp filters and cgroups (there&amp;rsquo;s probably more in there somewhere, too). Unfortunately, while seccomp has been around for a while, seccomp filters weren&amp;rsquo;t introduced in until v3.5, which means Snappy won&amp;rsquo;t run on our current phones.&lt;/p&gt;
&lt;p&gt;Until now (pretend I had a narrator voice).&lt;/p&gt;
&lt;p&gt;Seccomp filters have now been backported to and enabled on all supported Ubuntu phone kernels, allowing them to run Snappy and .snaps fully confined. These changes are hanging around in -proposed, and will be included in OTA9, which is currently set to release on January 27th.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s not to say that our current phones will ever move entirely to Snappy&amp;ndash; that&amp;rsquo;s not my call, and Snappy isn&amp;rsquo;t ready yet anyway. But at least the underlying support is there.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Prototype: A GUI-friendly Snappy</title>
      <link>https://kyrofa.com/posts/prototype-a-gui-friendly-snappy/</link>
      <pubDate>Tue, 03 Nov 2015 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/prototype-a-gui-friendly-snappy/</guid>
      <description>So this is the week of the Ubuntu Online Summit, and many of the sessions are discussing Snappy. As you may know, Snappy is currently pretty geared toward embedded, headless devices. However, it is the successor to Click, and eventually the phones will be based upon it. To drive that effort forward, a few colleagues and I had a session (update: I used to link to the video here, but the Summit server is no more) where we discussed the path forward for supporting snaps on other devices, specifically the phone and the desktop.</description>
      <content:encoded>&lt;p&gt;So this is the week of the Ubuntu Online Summit, and many of the sessions are discussing &lt;a href=&#34;https://ubuntu.com/core&#34;&gt;Snappy&lt;/a&gt;. As you may know, Snappy is currently pretty geared toward embedded, headless devices. However, it is the successor to Click, and eventually the phones will be based upon it. To drive that effort forward, a few colleagues and I had a session (&lt;strong&gt;update&lt;/strong&gt;: I used to link to the video here, but the Summit server is no more) where we discussed the path forward for supporting snaps on other devices, specifically the phone and the desktop.&lt;/p&gt;
&lt;p&gt;As Alejandro mentioned in the video, the current packaging situation sucks, and we know it. Application developers have to package their application as .deb for the desktop, .click for the phone, and .snap for embedded devices. Debian packaging isn&amp;rsquo;t going away&amp;ndash; we&amp;rsquo;ll always be based on Debian, but the current situation can be improved.&lt;/p&gt;
&lt;p&gt;Check out the first part of the video for the session. I gave a demo of a prototype tool that allows snaps to be installed and run alongside .debs and .clicks on both the phone and the desktop. Eventually we&amp;rsquo;d like Snappy to include such functionality, but until then, you can experiment with this yourself. We provided a link in IRC for a tutorial that walks you through exactly what I did in the video, but if you&amp;rsquo;re watching the video after the fact you probably didn&amp;rsquo;t get it. This post is for you.&lt;/p&gt;
&lt;p&gt;The result of the prototyping is called &amp;ldquo;Crackly&amp;rdquo; (snap&amp;hellip; crackle&amp;hellip; nevermind). It&amp;rsquo;s a wrapper around Snappy introducing some other limited functionality. Note that Crackly is not a product we&amp;rsquo;re proposing, simply a tool that enables our prototype, allowing Snaps to run transparently alongside .clicks (or .debs). It&amp;rsquo;ll go away as it&amp;rsquo;s superannuated by features in Snappy.&lt;/p&gt;
&lt;p&gt;This tutorial will walk you through installing and using Crackly. It has been tested on the current phone image and a Unity7-based Vivid desktop install.&lt;/p&gt;
&lt;h2 id=&#34;tutorial&#34;&gt;Tutorial&lt;/h2&gt;
&lt;p&gt;First of all, setup the Crackly PPA:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt-add-repository ppa:kyrofa/crackly
$ sudo apt-get update
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now install Crackly:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt-get install crackly
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That should pull in the relevant dependencies and install two binary utilities: the snappy CLI and crackly CLI. For this tutorial you won&amp;rsquo;t be using the snappy CLI directly (it&amp;rsquo;s used to drop privileges when unpacking the .snap), you&amp;rsquo;ll be using crackly.&lt;/p&gt;
&lt;p&gt;Now, you won&amp;rsquo;t be able to reach the Snappy store by default, so you&amp;rsquo;ll have to download (or create) a snap which you can sideload. Crackly specifically disallows installing services (since the phones are using Upstart, among other reasons), so make sure it only has binaries. If you don&amp;rsquo;t feel like making one, I &lt;a href=&#34;https://github.com/kyrofa/calculator-snap&#34;&gt;made one for you&lt;/a&gt;, you just have to build it into a snap:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ git clone https://github.com/kyrofa/calculator-snap
$ cd calculator-snap
$ snappy build .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;ll end up with &lt;code&gt;snappy-calculator_2.0.182.1_multi.snap&lt;/code&gt;, which can be installed on both armhf and amd64 (it&amp;rsquo;s QML&amp;ndash; update the package.yaml if you want more architectures). Transfer it to the device on which Crackly is installed.&lt;/p&gt;
&lt;p&gt;Alright, now on the device with Crackly, install the prerequisite for the calculator app (not necessary on the phone):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt-get install qmlscene
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now install the calculator snap:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo crackly install snappy-calculator_2.0.182.1_multi.snap
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, you should be able to open the dash (on Unity7) or the app scope (on Unity8), and you&amp;rsquo;ll notice a new &lt;code&gt;calculator&lt;/code&gt; app (with the icon from the QML calculator) which you should be able to launch. Doing so will run the app under the control of Ubuntu Core Launcher, exactly like any other Snappy-based system.&lt;/p&gt;
&lt;h2 id=&#34;limitations&#34;&gt;Limitations&lt;/h2&gt;
&lt;p&gt;There are a number of limitations to be aware of when experimenting with this prototype:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Crackly functionality is limited to installing/removing. No update or hw-assign is available, for example.&lt;/li&gt;
&lt;li&gt;If you&amp;rsquo;re running this on a current phone image, you&amp;rsquo;ll need it mounted read/write rather than read-only.&lt;/li&gt;
&lt;li&gt;If you&amp;rsquo;re running this on the current phone image, unless you&amp;rsquo;re using an arale (or newer) your kernel won&amp;rsquo;t support &lt;a href=&#34;https://kyrofa.com/posts/seccomp-filters-on-the-ubuntu-phone&#34;&gt;seccomp filters until OTA9&lt;/a&gt;. This is an important part of the Snappy confinement model and Ubuntu Core Launcher will refuse to launch without it. As a workaround, included in the Crackly PPA is an Ubuntu Core Launcher with a patch applied to launch even if seccomp filters aren&amp;rsquo;t available (note that this is an unofficial patch and will not be released).&lt;/li&gt;
&lt;li&gt;The calculator snap discussed above currently runs unconfined for various reasons. Still working on that bit.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded>
    </item>
    
    <item>
      <title>V3 Technology Awards: Vote for Canonical!</title>
      <link>https://kyrofa.com/posts/v3-technology-awards-vote-for-canonical/</link>
      <pubDate>Fri, 11 Sep 2015 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/v3-technology-awards-vote-for-canonical/</guid>
      <description>I&amp;rsquo;m pleased to announce that Canonical has made the shortlist in several categories for the V3 Technology Awards! Now it&amp;rsquo;s all in your hands: Go and Vote for Canonical! Note that voting closes at 5pm on Friday, October 9th.
The categories in which we made the shortlist are:
 Best cloud computing provider  We made this as an overall company: Canonical   Best business software provider  We also made this as an overall company: Canonical   Best Internet of Things product  Made this as Canonical Snappy Ubuntu Core    Thank you for your support!</description>
      <content:encoded>&lt;p&gt;I&amp;rsquo;m pleased to announce that Canonical has made the shortlist in several categories for the V3 Technology Awards! Now it&amp;rsquo;s all in your hands: Go and &lt;a href=&#34;http://www.surveygizmo.com/s3/2323021/V3-Technology-Awards-2015&#34;&gt;Vote for Canonical&lt;/a&gt;! Note that voting closes at 5pm on Friday, October 9th.&lt;/p&gt;
&lt;p&gt;The categories in which we made the shortlist are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Best cloud computing provider
&lt;ul&gt;
&lt;li&gt;We made this as an overall company: Canonical&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Best business software provider
&lt;ul&gt;
&lt;li&gt;We also made this as an overall company: Canonical&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Best Internet of Things product
&lt;ul&gt;
&lt;li&gt;Made this as Canonical Snappy Ubuntu Core&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thank you for your support!&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Snappy Scope: Progress Update</title>
      <link>https://kyrofa.com/posts/snappy-scope-progress-update/</link>
      <pubDate>Thu, 20 Aug 2015 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/snappy-scope-progress-update/</guid>
      <description>I&amp;rsquo;ve previously written about Snappy and Unity 8&amp;rsquo;s Snappy Scope, back when I was first getting started on it. Since then, both the scope and Ubuntu Core itself have come a long way, so I thought it was time for an update and a quick demo.
When I was working on Snappy back when I wrote the other post I was running Ubuntu Core virtually, since I was using my embedded devices for other tasks.</description>
      <content:encoded>&lt;p&gt;I&amp;rsquo;ve &lt;a href=&#34;https://kyrofa.com/posts/unity-8-s-snappy-scope&#34;&gt;previously written&lt;/a&gt; about Snappy and Unity 8&amp;rsquo;s &lt;a href=&#34;https://launchpad.net/unity-scope-snappy&#34;&gt;Snappy Scope&lt;/a&gt;, back when I was first getting started on it. Since then, both the scope and Ubuntu Core itself have come a long way, so I thought it was time for an update and a quick demo.&lt;/p&gt;
&lt;p&gt;When I was working on Snappy back when I wrote the other post I was running Ubuntu Core virtually, since I was using my embedded devices for other tasks. Since then, Canonical has sent me an Orange Matchbox to use, onto which I promptly installed Ubuntu Core. I was really hoping to be able to demo some hardware for you, but neither GPIO or I2C was supported, so I just used it to continue developing the Snappy Scope.&lt;/p&gt;
&lt;p&gt;The scope is now much more functional, having the ability to list installed and store snaps, as well as install and uninstall them. It&amp;rsquo;s only missing a few things at this point:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The ability to launch installed snaps (this is due to the .snap specification only having the definition of a service, not a launchable application).&lt;/li&gt;
&lt;li&gt;More details on the previews.&lt;/li&gt;
&lt;li&gt;Internationalization support.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thankfully, not long after I got to that point, support for I2C and GPIO was finally added to the Raspberry Pi 2 image, so I was able to put a decent demo together of both items:&lt;/p&gt;

&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
  &lt;iframe src=&#34;https://www.youtube-nocookie.com/embed/17nVOHrCh7Y&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; allowfullscreen title=&#34;YouTube Video&#34;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;If you want to get up and running with the scope, the instructions haven&amp;rsquo;t really changed since the previous post (i.e. read the README). It&amp;rsquo;s also installed out-of-the-box on Ubuntu Personal, although please remember that the image overall is very rough. If you want to give it a spin anyway, you can create one for KVM (it won&amp;rsquo;t work with VirtualBox because, well, Mir) like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo ubuntu-device-flash personal rolling --channel edge --output personal.img
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Post a comment if you have any questions, or hop on #snappy on Freenode.&lt;/p&gt;
&lt;h2 id=&#34;update&#34;&gt;Update&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve been asked a few times about the source code to PiGlow Top. It&amp;rsquo;s a project I wrote for this demonstration, and it&amp;rsquo;s &lt;a href=&#34;https://github.com/kyrofa/piglowtop&#34;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;update-2&#34;&gt;Update 2&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s no longer possible to create an Ubuntu Personal image from &lt;code&gt;ubuntu-device-flash&lt;/code&gt;. We&amp;rsquo;re switching some channels around and focusing on pocket desktop at the moment. Don&amp;rsquo;t worry, it&amp;rsquo;s not dead, just not being actively developed for the time being.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Travis CI and Sauce Labs</title>
      <link>https://kyrofa.com/posts/travis-ci-and-sauce-labs/</link>
      <pubDate>Sun, 05 Jul 2015 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/travis-ci-and-sauce-labs/</guid>
      <description>I think we can all agree that testing one&amp;rsquo;s Rails project is a good thing. I think we can also agree that those tests should be run on a regular basis (i.e. after every change). I think it&amp;rsquo;s a logical conclusion then that having a continuous integration environment setup for one&amp;rsquo;s project would be considered ideal. This is really pretty easy for open-source projects, since a number of companies provide free support for them.</description>
      <content:encoded>&lt;p&gt;I think we can all agree that testing one&amp;rsquo;s Rails project is a good thing. I think we can also agree that those tests should be run on a regular basis (i.e. after every change). I think it&amp;rsquo;s a logical conclusion then that having a continuous integration environment setup for one&amp;rsquo;s project would be considered ideal. This is really pretty easy for open-source projects, since a number of companies provide free support for them. The go-to CI for Rails projects seems to be Travis CI, but it has a limitation: browser testing with javascript is hard.&lt;/p&gt;
&lt;p&gt;The typical tool for Rails integration testing is &lt;a href=&#34;https://github.com/jnicklas/capybara&#34;&gt;Capybara&lt;/a&gt;. When I first got started with it, I began with the Selenium web driver for javascript tests since it was easy (Firefox just popped up and started running my tests). Unfortunately, when I wanted to get my project running with Travis CI, I quickly ran into a problem: Travis CI is headless. I couldn&amp;rsquo;t just launch Firefox and start running my tests.&lt;/p&gt;
&lt;h2 id=&#34;headless-option-phantomjs&#34;&gt;Headless option: PhantomJS&lt;/h2&gt;
&lt;p&gt;I did a little research and came across PhantomJS, which would allow me to run my javascript tests completely headless. It was easy to get integrated with Capybara and running in my testing setup, but it didn&amp;rsquo;t work the same way as Selenium&amp;ndash; several of my tests started failing, and I wasn&amp;rsquo;t sure why. Had I started out using PhantomJS, it probably would have been fine. Unfortunately, this was after my project was nearly feature-complete&amp;ndash; I had many, many tests, and I wasn&amp;rsquo;t interested in rewriting them.&lt;/p&gt;
&lt;h2 id=&#34;better-option-sauce-labs&#34;&gt;Better option: Sauce Labs&lt;/h2&gt;
&lt;p&gt;Fortunately, my work ethic is such that I&amp;rsquo;ll spend ages trying to do the least amount of work possible. That paid off: I came across &lt;a href=&#34;https://saucelabs.com/&#34;&gt;Sauce Labs&lt;/a&gt;. Not only could I continue using Selenium, but I could run my Selenium tests across an &lt;em&gt;enormous&lt;/em&gt; range of browsers and devices! I was thrilled, but my enthusiasm was quickly quelled by the lack of documentation.&lt;/p&gt;
&lt;h3 id=&#34;the-problem&#34;&gt;The Problem&lt;/h3&gt;
&lt;p&gt;I read the &lt;a href=&#34;http://docs.travis-ci.com/user/sauce-connect/&#34;&gt;documentation from Travis&lt;/a&gt;, but came away asking &amp;ldquo;How do I run my tests on multiple browsers?&amp;rdquo; I read the documentation from Sauce Labs only to realize it was basically the same document. I found some more documentation from Sauce Labs that introduced me to a gem for running multiple tests, only to learn after a while that I couldn&amp;rsquo;t use it with MiniTest. Also, that gem doesn&amp;rsquo;t work with Ruby 1.9.3, whereas the rest of my project worked with both 1.9.3 and 2.1. Using the gem would mean I couldn&amp;rsquo;t run 1.9.3 tests on Travis, which was a deal-breaker.&lt;/p&gt;
&lt;h3 id=&#34;the-solution&#34;&gt;The Solution&lt;/h3&gt;
&lt;p&gt;I finally figured out the secret: the Travis build matrix. It was actually pretty easy to do this combining the original Travis documentation for Sauce integration (linked above) with the build matrix environment variables.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s quickly discuss &lt;a href=&#34;http://docs.travis-ci.com/user/environment-variables/&#34;&gt;Travis environment variables&lt;/a&gt;. You can define them in your &lt;code&gt;.travis.yml&lt;/code&gt; file like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;env&lt;/span&gt;:
 - &lt;span style=&#34;color:#ae81ff&#34;&gt;FOO=bar&lt;/span&gt;
 - &lt;span style=&#34;color:#ae81ff&#34;&gt;BAZ=qux&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;My initial assumption was that the build in Travis would now have access to two environment variables: &lt;code&gt;FOO&lt;/code&gt; and &lt;code&gt;BAZ&lt;/code&gt;. That actually isn&amp;rsquo;t correct&amp;ndash; each item in the &lt;code&gt;env&lt;/code&gt; list causes a new Travis build to spin up! In other words, the above file would actually cause two Travis builds to begin, one with the &lt;code&gt;FOO&lt;/code&gt; variable defined, and one with the &lt;code&gt;BAZ&lt;/code&gt; variable defined. If I wanted the behavior I initially expected, I&amp;rsquo;d have to do this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;env&lt;/span&gt;:
 - &lt;span style=&#34;color:#ae81ff&#34;&gt;FOO=bar BAZ=qux&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s the key. I wanted a new build for each Sauce Labs combination I wanted to test, which meant that I needed a new &lt;code&gt;env&lt;/code&gt; item for each. That ended up looking something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#f92672&#34;&gt;env&lt;/span&gt;:
 &lt;span style=&#34;color:#75715e&#34;&gt;# Linux tests&lt;/span&gt;
 - &lt;span style=&#34;color:#ae81ff&#34;&gt;PLATFORM=&amp;#34;Linux&amp;#34; BROWSER=&amp;#34;firefox&amp;#34;&lt;/span&gt;
 - &lt;span style=&#34;color:#ae81ff&#34;&gt;PLATFORM=&amp;#34;Linux&amp;#34; BROWSER=&amp;#34;chrome&amp;#34;&lt;/span&gt;

 &lt;span style=&#34;color:#75715e&#34;&gt;# Mac OS X tests&lt;/span&gt;
 - &lt;span style=&#34;color:#ae81ff&#34;&gt;PLATFORM=&amp;#34;OS X 10.10&amp;#34; BROWSER=&amp;#34;firefox&amp;#34;&lt;/span&gt;
 - &lt;span style=&#34;color:#ae81ff&#34;&gt;PLATFORM=&amp;#34;OS X 10.10&amp;#34; BROWSER=&amp;#34;chrome&amp;#34;&lt;/span&gt;

 &lt;span style=&#34;color:#75715e&#34;&gt;# Windows tests&lt;/span&gt;
 - &lt;span style=&#34;color:#ae81ff&#34;&gt;PLATFORM=&amp;#34;Windows 7&amp;#34; BROWSER=&amp;#34;firefox&amp;#34;&lt;/span&gt;
 - &lt;span style=&#34;color:#ae81ff&#34;&gt;PLATFORM=&amp;#34;Windows 7&amp;#34; BROWSER=&amp;#34;chrome&amp;#34;&lt;/span&gt;
 - &lt;span style=&#34;color:#ae81ff&#34;&gt;PLATFORM=&amp;#34;Windows 8.1&amp;#34; BROWSER=&amp;#34;firefox&amp;#34;&lt;/span&gt;
 - &lt;span style=&#34;color:#ae81ff&#34;&gt;PLATFORM=&amp;#34;Windows 8.1&amp;#34; BROWSER=&amp;#34;chrome&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That gave me 8 different Travis builds with those corresponding environment variables defined. Now all I had to do was use them.&lt;/p&gt;
&lt;p&gt;I was able to tweak the information provided in the original Travis CI document about integrating with Sauce Labs, and came up with this to put in my &lt;code&gt;test_helper.rb&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# &amp;lt;snip&amp;gt;&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;# Only run with Sauce Labs if we&amp;#39;re running on Travis CI.&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;ENV&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;TRAVIS&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
   capabilities &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;Selenium&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;WebDriver&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;Remote&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;Capabilities&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;send &lt;span style=&#34;color:#66d9ef&#34;&gt;ENV&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;BROWSER&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;

   &lt;span style=&#34;color:#75715e&#34;&gt;# Set the browser version. If this is nil, Sauce Labs will use the latest version.&lt;/span&gt;
   capabilities&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;version &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;ENV&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;VERSION&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;

   &lt;span style=&#34;color:#75715e&#34;&gt;# Set the operation system.&lt;/span&gt;
   capabilities&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;platform &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;ENV&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PLATFORM&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;

   &lt;span style=&#34;color:#75715e&#34;&gt;# Set the tunnel ID to connect to the identified Sauce Connect tunnel from Travis.&lt;/span&gt;
   capabilities&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;tunnel-identifier&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;ENV&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;TRAVIS_JOB_NUMBER&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;

   &lt;span style=&#34;color:#75715e&#34;&gt;# Give the build a name in Sauce Labs so they aren&amp;#39;t just all &amp;#34;Unnamed Job.&amp;#34;&lt;/span&gt;
   capabilities&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Travis #&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;#{&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;ENV&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;TRAVIS_JOB_NUMBER&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;

   &lt;span style=&#34;color:#66d9ef&#34;&gt;Capybara&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;register_driver &lt;span style=&#34;color:#e6db74&#34;&gt;:selenium&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;app&lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;
       &lt;span style=&#34;color:#66d9ef&#34;&gt;Capybara&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;Selenium&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;Driver&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;new(app,
                                      &lt;span style=&#34;color:#e6db74&#34;&gt;browser&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;:remote&lt;/span&gt;,
                                      &lt;span style=&#34;color:#e6db74&#34;&gt;url&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;#{&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;ENV&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;SAUCE_USERNAME&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;#{&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;ENV&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;SAUCE_ACCESS_KEY&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;@ondemand.saucelabs.com/wd/hub&amp;#34;&lt;/span&gt;,
                                      &lt;span style=&#34;color:#e6db74&#34;&gt;desired_capabilities&lt;/span&gt;: capabilities)
   &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;

&lt;span style=&#34;color:#75715e&#34;&gt;# &amp;lt;snip&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, I had multiple Travis CI builds all talking to different Sauce Lab environments, running my tests. This setup has been working great for me so far, and I encourage you to give it a try.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Rails: Writing Engine Tests That Depend on Main Application Models</title>
      <link>https://kyrofa.com/posts/rails-writing-engine-tests-that-depend-on-main-application-models/</link>
      <pubDate>Tue, 02 Jun 2015 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/rails-writing-engine-tests-that-depend-on-main-application-models/</guid>
      <description>When developing a new Ruby on Rails project, one doesn&amp;rsquo;t always start from scratch. Often one first searches for an open-source project that will at least get one part of the way there. For example, one might want to do something as simple a create a website with a blog as well as image galleries. The first step in this journey might be to find some open-source blog software for Rails, e.</description>
      <content:encoded>&lt;p&gt;When developing a new Ruby on Rails project, one doesn&amp;rsquo;t always start from scratch. Often one first searches for an open-source project that will at least get one part of the way there. For example, one might want to do something as simple a create a website with a blog as well as image galleries. The first step in this journey might be to find some open-source blog software for Rails, e.g. &lt;a href=&#34;https://publify.github.io/&#34;&gt;Publify&lt;/a&gt; (previously called Typo). Great, one has a blog!&lt;/p&gt;
&lt;p&gt;Now what about that image gallery stuff? One would surely like to utilize the same authentication/account structure that is in use in the blogging software, but there&amp;rsquo;s a problem: Projects such as Publify are distributed as an entire Rails application. Yes, it&amp;rsquo;s of course open-source, but it was written to be one&amp;rsquo;s entire website. Adding image galleries means one needs to figure out and alter the Publify source code, and that&amp;rsquo;s just not cool.&lt;/p&gt;
&lt;h2 id=&#34;the-solution-mountable-engines&#34;&gt;The Solution: Mountable Engines&lt;/h2&gt;
&lt;p&gt;Actually, that exact situation is what led to my writing &lt;a href=&#34;https://github.com/kyrofa/proclaim&#34;&gt;Proclaim&lt;/a&gt;. I wanted something that didn&amp;rsquo;t try to be my entire website, and just focused on doing what it was supposed to do: provide a blog. It was written as a mountable engine, and it doesn&amp;rsquo;t even include users or authentication&amp;ndash; it will make use of whatever the main application is using.&lt;/p&gt;
&lt;h2 id=&#34;the-resulting-difficulties&#34;&gt;The Resulting Difficulties&lt;/h2&gt;
&lt;p&gt;That desire quickly led to a testing issue, though. The blog &lt;code&gt;Post&lt;/code&gt; model doesn&amp;rsquo;t need to know anything about its author, and the engine doesn&amp;rsquo;t need to deal with accounts in any way, but it makes sense for posts to have an author, so the solution is to make the class for the author configurable and we say that the &lt;code&gt;Post&lt;/code&gt; model &lt;code&gt;belongs_to&lt;/code&gt; it. Then the main application can configure that author class and the engine will then use whatever the main application configured. However, how does one write tests for the &lt;code&gt;Post&lt;/code&gt; model if the engine doesn&amp;rsquo;t contain a model for the author?&lt;/p&gt;
&lt;p&gt;Let me answer that with a quick tutorial (see the &lt;a href=&#34;http://guides.rubyonrails.org/engines.html&#34;&gt;official Rails Engines guide&lt;/a&gt; for more information).&lt;/p&gt;
&lt;h2 id=&#34;tutorial&#34;&gt;Tutorial&lt;/h2&gt;
&lt;h3 id=&#34;create-engine-with-posts&#34;&gt;Create Engine with Posts&lt;/h3&gt;
&lt;p&gt;First, create your mountable engine:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~
$ rails plugin new blorgh --mountable
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this tutorial, all we care about is the cross-over between engine and main application, and for our engine that happens on the &lt;code&gt;Post&lt;/code&gt; model (we don&amp;rsquo;t need any comments or anything). So create a scaffold for your &lt;code&gt;Post&lt;/code&gt; model:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/blorgh/
$ rails g scaffold post author_id:integer title:string text:text
$ rake db:migrate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice the &lt;code&gt;author_id&lt;/code&gt; bit. That&amp;rsquo;s where the &lt;code&gt;Post&lt;/code&gt; will &lt;code&gt;belong_to&lt;/code&gt; an author, even though the author class itself is not defined within the engine. Let&amp;rsquo;s add that &lt;code&gt;belongs_to&lt;/code&gt; logic to the &lt;code&gt;Post&lt;/code&gt; model now:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;module&lt;/span&gt; Blorgh
  &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Post&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;ActiveRecord&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;Base&lt;/span&gt;
     belongs_to &lt;span style=&#34;color:#e6db74&#34;&gt;:author&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;required&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;class_name&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;User&amp;#34;&lt;/span&gt;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is saying that the &lt;code&gt;Post&lt;/code&gt; model must belong to an author, which happens to be a model called &lt;code&gt;User&lt;/code&gt; (no, such a model doesn&amp;rsquo;t exist).&lt;/p&gt;
&lt;h3 id=&#34;write-tests&#34;&gt;Write Tests&lt;/h3&gt;
&lt;p&gt;Alright, we have a &lt;code&gt;Post&lt;/code&gt; model, so before we start fleshing it out, let&amp;rsquo;s write some tests (which is really what I&amp;rsquo;m writing about anyway). We know that a &lt;code&gt;Post&lt;/code&gt; should have an author, so let&amp;rsquo;s write a test to ensure that a &lt;code&gt;Post&lt;/code&gt; cannot be saved if it doesn&amp;rsquo;t have an author. The scaffold generated a &lt;code&gt;blorgh/test/models/blorgh/post_test.rb&lt;/code&gt; file for us, so we might as well use that:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;require &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;test_helper&amp;#39;&lt;/span&gt;

&lt;span style=&#34;color:#66d9ef&#34;&gt;module&lt;/span&gt; Blorgh
  &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;PostTest&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;ActiveSupport&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;TestCase&lt;/span&gt;
     test &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ensure author is required&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
        post &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;Post&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;new(&lt;span style=&#34;color:#e6db74&#34;&gt;title&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Foo&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;text&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Bar&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;author_id&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;)
        refute post&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;save, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Post should require an author_id&amp;#34;&lt;/span&gt;

        &lt;span style=&#34;color:#75715e&#34;&gt;# Author with 12345 shouldn&amp;#39;t exist&lt;/span&gt;
        post &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;Post&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;new(&lt;span style=&#34;color:#e6db74&#34;&gt;title&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Foo&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;text&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Bar&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;author_id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;12345&lt;/span&gt;)
        refute post&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;save, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Post should require a valid author&amp;#34;&lt;/span&gt;
     &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pretty simple, right?&lt;/p&gt;
&lt;h3 id=&#34;first-test-run&#34;&gt;First Test Run&lt;/h3&gt;
&lt;p&gt;Alright, time to run the tests:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/blorgh/
$ rake test
&amp;lt;snip&amp;gt;
1&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; Error:
Blorgh::PostTest#test_ensure_author_is_required:
NameError: uninitialized constant Blorgh::Post::User
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Right, so: the &lt;code&gt;Post&lt;/code&gt; model &lt;code&gt;belongs_to&lt;/code&gt; a &lt;code&gt;User&lt;/code&gt; model, but the &lt;code&gt;User&lt;/code&gt; model doesn&amp;rsquo;t exist. Where should it go? We&amp;rsquo;ve already decided that it doesn&amp;rsquo;t belong in our engine. Fortunately, Rails is way ahead of you.&lt;/p&gt;
&lt;h3 id=&#34;the-dummy-application&#34;&gt;The Dummy Application&lt;/h3&gt;
&lt;p&gt;Your engine has to be mounted before it can do anything, including run tests. For this purpose, there&amp;rsquo;s an entire Rails application pre-generated for you in &lt;code&gt;blorgh/test/dummy/&lt;/code&gt;. This is a &lt;em&gt;perfect&lt;/em&gt; place for the &lt;code&gt;User&lt;/code&gt; model to go:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/blorgh/test/dummy
$ rails g model User name:string
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now run the dummy application&amp;rsquo;s migrations:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/blorgh/
$ rake db:migrate RAILS_ENV&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;test
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You should notice the &amp;ldquo;CreateUsers&amp;rdquo; migration running.&lt;/p&gt;
&lt;h3 id=&#34;second-test-run&#34;&gt;Second Test Run&lt;/h3&gt;
&lt;p&gt;Alright, let&amp;rsquo;s try our test again:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd ~/blorgh/
$ rake test
&amp;lt;snip&amp;gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; failures, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; errors, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; skips
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Success!&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;The use of the dummy application took some getting used to, and I don&amp;rsquo;t feel like it was well-explained in the documentation. I was initially under the impression that, if I wrote models in the dummy application, I would also have to write tests in the dummy application, and that just seemed nasty. Comments in the Rails mailing list about how the dummy application shouldn&amp;rsquo;t be committed to version control didn&amp;rsquo;t help&amp;ndash; I obviously disagree with that.&lt;/p&gt;
&lt;p&gt;In the end, you&amp;rsquo;ll find that the dummy application is a perfect fit for testing the crossover between your engine and the main application. The test written in this tutorial didn&amp;rsquo;t use any fixtures or factories for simplicity, but note: if you use &lt;a href=&#34;https://github.com/thoughtbot/factory_bot_rails&#34;&gt;FactoryBot&lt;/a&gt;, don&amp;rsquo;t put factories in the dummy application (FactoryBot won&amp;rsquo;t look there). Put factories for models defined in the dummy (e.g. the &lt;code&gt;User&lt;/code&gt; model) in &lt;code&gt;blorgh/test/factories/&lt;/code&gt; (not &lt;code&gt;blorgh/test/factories/blorgh/&lt;/code&gt;, since it isn&amp;rsquo;t contained within the engine).&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Unity 8&#39;s Snappy Scope</title>
      <link>https://kyrofa.com/posts/unity-8-s-snappy-scope/</link>
      <pubDate>Sun, 24 May 2015 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/unity-8-s-snappy-scope/</guid>
      <description>I recently began a new position at Canonical. I joined this company in particular to give myself more opportunities for growth. Now, when I say that, people tend to assume I&amp;rsquo;m talking about a managerial track. I&amp;rsquo;m not sure why they tend to jump there, but it&amp;rsquo;s not what I&amp;rsquo;m referring to here. I&amp;rsquo;m talking about technical growth. I feel at the top of my game when I&amp;rsquo;m learning something brand new, and I needed a company that facilitated that.</description>
      <content:encoded>&lt;p&gt;I recently began a new position at Canonical. I joined this company in particular to give myself more opportunities for growth. Now, when I say that, people tend to assume I&amp;rsquo;m talking about a managerial track. I&amp;rsquo;m not sure why they tend to jump there, but it&amp;rsquo;s not what I&amp;rsquo;m referring to here. I&amp;rsquo;m talking about technical growth. I feel at the top of my game when I&amp;rsquo;m learning something brand new, and I needed a company that facilitated that. Canonical has delivered.&lt;/p&gt;
&lt;p&gt;My background is quite heavily focused on C++. My resume revolves around it, and the interviews each probed the depths of my C++ understanding. So, as logic dictates, my first technical tasking is in&amp;ndash; you guessed it&amp;ndash; Go.&lt;/p&gt;
&lt;p&gt;This is fantastic. Not that I particularly care about Go (I&amp;rsquo;d never used it before now), but because it broadens my horizons. I&amp;rsquo;ve been entrenched in the Google-test-driven C++ world for a while, so it&amp;rsquo;s nice to be able to dig into a new language, with new testing idioms. This can&amp;rsquo;t not make me a better all-around engineer.&lt;/p&gt;
&lt;p&gt;I joined the company amidst the fervor of &lt;a href=&#34;https://snapcraft.io/&#34;&gt;snaps&lt;/a&gt; and &lt;a href=&#34;https://ubuntu.com/core&#34;&gt;Ubuntu Core&lt;/a&gt;&amp;ndash; a transactionally-updated version of Ubuntu targeted mainly at embedded devices. In order to be transactionally updated, Ubuntu Core uses a different packaging mechanism than regular-old Ubuntu. Ubuntu, as a Debian derivative, obviously uses .debs, but Ubuntu Core uses a packaging format called Snap, which is the successor of Click (the packaging format currently used on the Ubuntu Phone). In addition, a large number of Ubuntu Core components are written in Go, thus the choice of language for my piece.&lt;/p&gt;
&lt;h2 id=&#34;quick-ubuntu-core-background&#34;&gt;Quick Ubuntu Core Background&lt;/h2&gt;
&lt;p&gt;Ubuntu Core is brand new, and pretty beta. Things are changing quickly, and I&amp;rsquo;m pretty new to the whole thing, so take what I say here with a grain of salt.&lt;/p&gt;
&lt;p&gt;The master plan, as I understand it, is for Ubuntu to dominate the Internet of Things (IoT). The problem with embedded devices today is that one typically buys them from the manufacturer with some firmware installed, and then that device runs forever without anyone touching it again. At first glance that&amp;rsquo;s a good thing&amp;ndash; the device should run forever. But the side effect of this is that the software on the device never gets updated. A good example of how this is a problem is with routers: a public-facing device, with security holes, that never gets updated to fix the holes. Pretending that the manufacturer even releases new software to fix the problems, the consumer can&amp;rsquo;t be bothered to: A) check to see that there is an update, or B) apply said update.&lt;/p&gt;
&lt;p&gt;Ubuntu Core is attempting to solve this problem by changing these manual firmware flashes to software updates that can be safely automated, because they&amp;rsquo;re completely transactional, i.e. they can be reversed if they cause problems.&lt;/p&gt;
&lt;p&gt;In addition to this, because the embedded device is running Ubuntu, you should be able to install whatever applications you want. Of course, since these are embedded devices, you have no way to do that unless you want to SSH into them or something. We thought of that with Ubuntu Core: You can use WebDM to configure each device remotely!&lt;/p&gt;
&lt;h2 id=&#34;ubuntu-core--ubuntu-mobile&#34;&gt;Ubuntu Core + Ubuntu Mobile&lt;/h2&gt;
&lt;p&gt;Okay, so Ubuntu Core has a pretty solid story behind it. So does Ubuntu Mobile. However, Canonical wants to fuse the two together. This makes sense&amp;ndash; they have very similar components already (security model, transactional updates, app store, etc.), but they still have a lot of differences, particularly regarding the packaging and software install/update/run model. That&amp;rsquo;s (partially) where I come in.&lt;/p&gt;
&lt;h2 id=&#34;snappy-scope&#34;&gt;&amp;ldquo;Snappy Scope&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;Ubuntu Core makes the assumption that the user will do nothing locally (since an embedded device is also typically a headless device). Adding Ubuntu Mobile changes the game a bit, since a user is now doing everything locally. One of the first components we need is essentially a local-only WebDM&amp;ndash; a local app store. Since Ubuntu Mobile uses Unity 8, it makes sense for this to be a Unity Scope.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;But wait,&amp;rdquo; you&amp;rsquo;re thinking. &amp;ldquo;Doesn&amp;rsquo;t Ubuntu Mobile already have an app store?&amp;rdquo; Indeed it does, but that scope is for installing Click packages; we need one for snaps.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;But wait,&amp;rdquo; you may also be thinking. &amp;ldquo;The Unity Scope API documentation is only available for C++. Didn&amp;rsquo;t you say this was in Go?&amp;rdquo; Ah, you are astute! This isn&amp;rsquo;t officially supported just yet, but my colleagues James Henstridge and Xavi Garcia have been slaving away on &lt;a href=&#34;https://launchpad.net/go-unityscopes&#34;&gt;Go bindings&lt;/a&gt; for the Unity Scope API, and they are quite functional.&lt;/p&gt;
&lt;h2 id=&#34;development&#34;&gt;Development&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;re hoping to throw together a proof-of-concept very soon, which gave us a hudle really early-on: We don&amp;rsquo;t want to duplicate the effort that has gone into WebDM. WebDM is also written in Go, so we were hoping we could at least share libraries or something. Unfortunately, WebDM wasn&amp;rsquo;t written that way&amp;ndash; it has a REST API for the web interface, and that&amp;rsquo;s it.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s okay. Ubuntu Core is indeed pretty beta, so we&amp;rsquo;ll work on getting WebDM to where it needs to go. Until then, we still don&amp;rsquo;t want to duplicate effort talking to the overarching Ubuntu Store API, so in our proof-of-concept we&amp;rsquo;ll just be using the existing WebDM REST API. We&amp;rsquo;re doing this in a very modular way so we can rip it out easily later.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re still very early-on in the development process, but you can &lt;a href=&#34;https://launchpad.net/unity-scope-snappy&#34;&gt;check it out&lt;/a&gt; (and even contribute!) if you like.&lt;/p&gt;
&lt;h2 id=&#34;how-to-test&#34;&gt;How to test&lt;/h2&gt;
&lt;p&gt;First of all, you should check out the project&amp;rsquo;s README. It will be more up-to-date than this post.&lt;/p&gt;
&lt;p&gt;Prerequisites:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ubuntu 15.04 (Vivid)&lt;/li&gt;
&lt;li&gt;Ubuntu Core running somewhere (I just put it in a VM)&lt;/li&gt;
&lt;li&gt;WebDM must be installed&lt;/li&gt;
&lt;li&gt;WebDM must be accessible from where the scope is running&lt;/li&gt;
&lt;li&gt;Unity Scope Tool&lt;/li&gt;
&lt;li&gt;Golang&lt;/li&gt;
&lt;li&gt;Bazaar&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Install the required packages:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt-get install unity-scope-tool golang-go bzr
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now setup your Go workspace. This is what I use:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ mkdir -p ~/src/go &lt;span style=&#34;color:#75715e&#34;&gt;# Create the workspace directory&lt;/span&gt;

$ echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;export GOPATH=~/src/go&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; ~/.bashrc &lt;span style=&#34;color:#75715e&#34;&gt;# Make this environment whenever you login&lt;/span&gt;
$ echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;export PATH=\$GOPATH/bin:\$PATH&amp;#34;&lt;/span&gt;

$ source ~/.bashrc &lt;span style=&#34;color:#75715e&#34;&gt;# Make it your environment now&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now grab the scope itself:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd $GOPATH
$ go get launchpad.net/unity-scope-snappy/snappy-scope
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This should result in a &lt;code&gt;snappy-scope&lt;/code&gt; binary being placed into &lt;code&gt;$GOPATH/bin/&lt;/code&gt;. However, it can&amp;rsquo;t be run directly&amp;ndash; this is where the Unity Scope Tool comes in.&lt;/p&gt;
&lt;p&gt;Each Unity Scope requires a .ini file describing itself and how to launch it, and Go scopes are no different. The Snappy Scope&amp;rsquo;s .ini file is alongside the source code, so launch it like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ cd $GOPATH
$ unity-scope-tool src/launchpad.net/unity-scope-snappy/snappy-scope/snappy-scope.ini
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The scope by default uses the &lt;code&gt;webdm.local&lt;/code&gt; hostname to communicate with WebDM. If that&amp;rsquo;s not working, edit the snappy-scope.ini file to tell it where to look for WebDM. Something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[ScopeConfig]
&amp;lt;snip&amp;gt;
ScopeRunner = $GOPATH/bin/snappy-scope --runtime %R --scope %S -webdm=&amp;lt;ip:port of webdm&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As you can see, the functionality of the scope is currently quite limited. At the time of this writing, all it can do is display applications from the store. I&amp;rsquo;m currently working on making those applications installable.&lt;/p&gt;
&lt;p&gt;I haven&amp;rsquo;t yet noticed anything missing from the Go Scope bindings. Definitely worth checking out!&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>VirtualBox: Internet Access With Host-Only Network</title>
      <link>https://kyrofa.com/posts/virtualbox-internet-access-with-host-only-network/</link>
      <pubDate>Wed, 11 Feb 2015 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/virtualbox-internet-access-with-host-only-network/</guid>
      <description>I ran into the need to access the internet from a VirtualBox VM via host-only networking. Of course, you&amp;rsquo;re saying &amp;ldquo;There&amp;rsquo;s a better way to do that!&amp;rdquo; And of course, I agree: with no other considerations, the best way to do this is to simply add another NIC to the VM configured as NAT instead of host-only. However, I&amp;rsquo;m trying to test out some new networking ideas for my OpenStack Network node, and it doesn&amp;rsquo;t have a NIC configured with NAT.</description>
      <content:encoded>&lt;p&gt;I ran into the need to access the internet from a VirtualBox VM via host-only networking. Of course, you&amp;rsquo;re saying &amp;ldquo;There&amp;rsquo;s a better way to do that!&amp;rdquo; And of course, I agree: with no other considerations, the best way to do this is to simply add another NIC to the VM configured as NAT instead of host-only. However, I&amp;rsquo;m trying to test out some new networking ideas for my OpenStack Network node, and it doesn&amp;rsquo;t have a NIC configured with NAT. Since I&amp;rsquo;m trying to reproduce a production system in VirtualBox for testing purposes (which is how I typically use it), it stands to reason that I want the VM setup as close to the real thing as possible.&lt;/p&gt;
&lt;p&gt;This is actually pretty easy, especially after reading Ubuntu&amp;rsquo;s &lt;a href=&#34;https://help.ubuntu.com/community/Internet/ConnectionSharing&#34;&gt;Connection Sharing wiki page&lt;/a&gt;. First, consider my development setup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Development laptop NICs:
&lt;ul&gt;
&lt;li&gt;wlan0: Wifi internet connection&lt;/li&gt;
&lt;li&gt;vboxnet0: VirtualBox&amp;rsquo;s host-only interface (creating these is well-documented). I disabled DHCP to reproduce production setup.
&lt;ul&gt;
&lt;li&gt;IP is 192.168.56.1&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;VirtualBox VM NIC:
&lt;ul&gt;
&lt;li&gt;eth0: Host-only interface connecting to laptop&amp;rsquo;s vboxnet0.
&lt;ul&gt;
&lt;li&gt;static IP: 192.168.56.100/24&lt;/li&gt;
&lt;li&gt;gateway: 192.168.56.1 (i.e. development laptop)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Normally, in this setup, the VM would attempt to use the development laptop as a NAT gateway for external access, but since our laptop isn&amp;rsquo;t setup that way, the VM can&amp;rsquo;t see the internet nor is it accessible from the internet. I didn&amp;rsquo;t care about the latter&amp;ndash; I just needed internet access from the VM. That&amp;rsquo;s as simple as setting up IP forwarding on the development laptop and enabling a few firewall (iptables) rules to setup the NAT.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s start with some iptables rules. Note that these will be flushed upon reboot. That&amp;rsquo;s not important to me, but if you want these settings to persist it&amp;rsquo;s not difficult, and is well-documented elsewhere (start with Ubuntu&amp;rsquo;s wiki, linked above).&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s first add a rule to forward new connections from the host-only network:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo iptables -A FORWARD -o wlan0 -i vboxnet0 -s 192.168.56.0/24 -m conntrack --ctstate NEW -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now add a rule to forward already-established and related connections:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now flush the NAT table and add a new rule to it to masquerade any packets flowing out of wlan0 to look as if they&amp;rsquo;re coming from the laptop (i.e. &amp;ldquo;do NATing&amp;rdquo;):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo iptables -t nat -F POSTROUTING
$ sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At this point, the firewall is all setup to take care of NATing traffic from vboxnet0 to wlan0, but we still need to allow packet forwarding to occur at the kernel level. Again, the way we&amp;rsquo;re doing it here is temporary (will be gone at reboot):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo sh -c &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;echo 1 &amp;gt; /proc/sys/net/ipv4/ip_forward&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, you should be able to access resources on the internet from the VM:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ ping 8.8.8.8
PING 8.8.8.8 &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;8.8.8.8&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; 56&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;84&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; bytes of data.
&lt;span style=&#34;color:#ae81ff&#34;&gt;64&lt;/span&gt; bytes from 8.8.8.8: icmp_seq&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; ttl&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;56&lt;/span&gt; time&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;14.4 ms
&lt;span style=&#34;color:#ae81ff&#34;&gt;64&lt;/span&gt; bytes from 8.8.8.8: icmp_seq&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt; ttl&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;56&lt;/span&gt; time&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;14.6 ms
&lt;span style=&#34;color:#ae81ff&#34;&gt;64&lt;/span&gt; bytes from 8.8.8.8: icmp_seq&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt; ttl&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;56&lt;/span&gt; time&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;15.3 ms
^C
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note, however, that domain names won&amp;rsquo;t work, since we didn&amp;rsquo;t set that up. You know you can access 8.8.8.8 (Google&amp;rsquo;s nameserver), so you may as well use that. Then you&amp;rsquo;re good to go!&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>The Face Detector</title>
      <link>https://kyrofa.com/posts/the-face-detector/</link>
      <pubDate>Fri, 31 Jan 2014 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/the-face-detector/</guid>
      <description>Several years ago, I was new to my specific group at work. I was working on a combination of image processing and a GUI for keeping track of specific objects in the camera&amp;rsquo;s view. I started on the GUI first, writing a server that received object locations and handed them off to the GUI which displayed them in a nifty way. I was using Qt and QML, which I have used in numerous personal projects, so I was pretty familiar and confident with what I was doing.</description>
      <content:encoded>&lt;p&gt;Several years ago, I was new to my specific group at work. I was working on a combination of image processing and a GUI for keeping track of specific objects in the camera&amp;rsquo;s view. I started on the GUI first, writing a server that received object locations and handed them off to the GUI which displayed them in a nifty way. I was using Qt and QML, which I have used in numerous personal projects, so I was pretty familiar and confident with what I was doing. In order to test/develop the GUI I threw a quick C++ program together to create red and blue OpenCV images and fire them over the network.&lt;/p&gt;
&lt;p&gt;Right as I got close to getting the GUI far enough along that I was ready for real images, my supervisor came in and said that we had our project sponsors coming by to see how we were getting along. He wanted to show them my GUI work, and wanted it working with detecting faces in a live camera feed. Oh, and they were coming day after tomorrow.&lt;/p&gt;
&lt;p&gt;What? I had never really done much image processing before (other than one class in college), and had NEVER used OpenCV before (other than generating red and blue images). Now I needed to throw something together in a day?&lt;/p&gt;
&lt;p&gt;So I started doing some research. It turned out that Haar-cascade-based face detection was pretty easy in OpenCV, but it was too slow to do every frame in my case. So I went with initially detecting faces via Haar cascades, and then handed it off to a color tracker (based on Camshift). It wasn&amp;rsquo;t great, but it was acceptable for something I threw together in half a day. I spent the other half a day tying it together with the GUI.&lt;/p&gt;
&lt;p&gt;With the Camshift-based color tracker, one needed to calibrate it for the color you want to track (and toss out unrelated colors). So the day of the demo, I set my stuff up where I wanted and played with the calibration until it detected my Italian face quite nicely. I saved those parameters so they would be used every time the detector/tracker software was run. Multiple people came by throughout the demo day&amp;ndash; the setup was a hit! The initial face detection worked, and then people had fun trying to ditch the color tracker by trying to get it to track their hand, etc.&lt;/p&gt;
&lt;p&gt;Then my supervisor came in with the sponsor in tow. He was black.&lt;/p&gt;
&lt;p&gt;He stood in front of the camera with the other people playing with it, and it wouldn&amp;rsquo;t track him. He tried getting close: no cigar. Tilting his head&amp;hellip; nada. I exchanged awkward glances with my coworkers and tapped the camera like some idiot. &amp;ldquo;Huh&amp;hellip; looks like it&amp;rsquo;s not working anymore.&amp;rdquo; I quickly quit the program so he wouldn&amp;rsquo;t notice it tracking other people on my screen. I had no idea what to say. I had made a racist face detector.&lt;/p&gt;
&lt;p&gt;He finally stood up and said &amp;ldquo;Huh&amp;hellip; well cool,&amp;rdquo; and left. We&amp;rsquo;re still funded. Now that I know more, I would have used something like OpenTLD.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>GitLab 6.3 with Apache and Passenger. On an ARM</title>
      <link>https://kyrofa.com/posts/gitlab-6-3-with-apache-and-passenger-on-an-arm/</link>
      <pubDate>Mon, 25 Nov 2013 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/gitlab-6-3-with-apache-and-passenger-on-an-arm/</guid>
      <description>As I&amp;rsquo;ve mentioned in previous posts, I run all my websites on a MiraBox&amp;ndash; my nifty, low-power ARM server. As awesome as it is having a low-power server, it does make things more difficult. I&amp;rsquo;m a Ruby on Rails developer, and I run three websites on this thing, along with a gitolite installation so I can host my own code. However, I recently began a few projects collaborating with other people, and as the projects grow, gitolite is not the collaboration platform of success.</description>
      <content:encoded>&lt;p&gt;As I&amp;rsquo;ve mentioned in previous posts, I run all my websites on a &lt;a href=&#34;http://www.globalscaletechnologies.com/p-58-mirabox-development-kit.aspx&#34;&gt;MiraBox&lt;/a&gt;&amp;ndash; my nifty, low-power ARM server. As awesome as it is having a low-power server, it does make things more difficult. I&amp;rsquo;m a Ruby on Rails developer, and I run three websites on this thing, along with a gitolite installation so I can host my own code. However, I recently began a few projects collaborating with other people, and as the projects grow, gitolite is not the collaboration platform of success. I need groups and better permissions. I need issue tracking. I need wiki pages. I need &lt;a href=&#34;http://gitlab.org/&#34;&gt;GitLab&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s only one problem. If you follow its installation procedure, GitLab does not support the ARM architecture. Rather, gems upon which GitLab depends do not support the ARM architecture. Namely: &lt;a href=&#34;https://github.com/cowboyd/therubyracer&#34;&gt;therubyracer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Server-side Javascript has long been a nightmare on ARM. therubyracer depends on the &lt;a href=&#34;https://github.com/cowboyd/libv8&#34;&gt;libv8&lt;/a&gt; gem, which straight up cannot build on ARM. Which is annoying, since without too much convincing, V8 itself can build on ARM (even though it uses GYP, a build system that I simply cannot figure out).&lt;/p&gt;
&lt;p&gt;I tried participating in therubyracer and libv8 gem development, but there&amp;rsquo;s not a lot of momentum behind ARM development there and later versions of V8 simply don&amp;rsquo;t compile with the old GCC in Squeeze anymore, so I gave up and punted: Hello &lt;a href=&#34;http://nodejs.org/&#34;&gt;Node.js&lt;/a&gt;. With some code tweaks, I was able to get THAT installed on my MiraBox. I then removed therubyracer from my Gemfile and Gemfile.lock, and was able to follow the GitLab installation directions. Well&amp;hellip; mostly.&lt;/p&gt;
&lt;p&gt;As I said before, I already had three different websites on my server using Apache and Passenger&amp;ndash; I refuse to also use Nginx and Puma or Unicorn or whatever the kids are using these days. That made things a little harder though&amp;hellip; was there something special about Puma/Unicorn? Did I need it? These were questions for Google.&lt;/p&gt;
&lt;p&gt;&amp;hellip;&lt;/p&gt;
&lt;p&gt;No, I take that back&amp;ndash; with an utter lack of results other than a few mentions of Apache with no one talking about Puma or Unicorn, they were questions for the mailing list.&lt;/p&gt;
&lt;p&gt;&amp;hellip;&lt;/p&gt;
&lt;p&gt;No&amp;hellip; I take that back&amp;ndash; no one ever responded. So, I had to figure it out on my own.&lt;/p&gt;
&lt;p&gt;It was a while ago that I learned the lesson &lt;a href=&#34;https://kyrofa.com/posts/qemu-mirabox-emulation&#34;&gt;not to experiment&lt;/a&gt; with this type of stuff on a live server. However, I took it even a step further. I had previous experience setting up a GitLab install, but that experience only involved following the GitLab installation instructions to a T. I wanted to do a few things differently in this situation: namely, I wanted to use Apache instead of NginX, I wanted to use Passenger instead of Puma/Unicorn, and I needed to use Node instead of therubyracer. Since I didn&amp;rsquo;t even know if this setup would work on the best of systems, I decided that testing this setup for the first time on a crawling ARM emulation wasn&amp;rsquo;t the best way forward. I wanted to see if this worked on a more powerful, normal machine. So I threw a server installation together in VirtualBox, using Ubuntu 12.04 64-bit.&lt;/p&gt;
&lt;p&gt;I followed the GitLab installation instructions, but I skipped a few parts this time. GitLab 6.3 uses Unicorn (whereas some previous releases used Puma), and I specifically did not setup Unicorn at all&amp;ndash; I left its configuration file at &amp;ldquo;unicorn.rb.example,&amp;rdquo; which I assume means it doesn&amp;rsquo;t run. This required an alteration to the gitlab init script, since it runs both Sidekiq and Unicorn. I simply removed all mention of Unicorn, and rewrote the script to use Sidekiq only.&lt;/p&gt;
&lt;p&gt;Also, obviously, instead of setting up an NginX configuration for GitLab, I setup an Apache2 virtual host. Finally, before I ran the bundler to deploy the gems, I removed therubyracer from the Gemfile (and Gemfile.lock) and installed the newest Node.js from source. This setup actually worked perfectly, first try. There was hope! Also, that answered the question of Unicorn/Puma: They are not necessary for GitLab to function.&lt;/p&gt;
&lt;p&gt;I then moved to duplicating each of these steps on the QEMU-emulated ARM server. That took ages, I&amp;rsquo;ll tell you. And after I finished I got nothing. It spun for ages and then said that the server didn&amp;rsquo;t respond. Okay&amp;hellip; so not as good as the VirtualBox version. My investigation started in the GitLab logs. It actually looked like things were happening, but then Firefox always came back with &amp;ldquo;server didn&amp;rsquo;t respond.&amp;rdquo; After a little more digging I realized that everything did indeed look fine&amp;ndash; the server just didn&amp;rsquo;t respond fast enough.&lt;/p&gt;
&lt;p&gt;After some searching around, I realized that with Passenger 4.0.20 they introduced the &amp;ldquo;PassengerStartTimeout&amp;rdquo; option, which allows one to extend the loading window giving a good-sized application like GitLab time to fire everything up. I set it to 60000 (time out now, I dare you). Success!&lt;/p&gt;
&lt;p&gt;It was time to put GitLab on my real server.&lt;/p&gt;
&lt;p&gt;I went through the same steps&amp;ndash; didn&amp;rsquo;t configure Unicorn and configured an Apache2 virtual host for Gitlab:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;VirtualHost *:80&amp;gt;
   ServerName www.url.com
   ServerAlias url.com
   DocumentRoot /home/git/gitlab/public
   &amp;lt;Directory /home/git/gitlab/public/&amp;gt;
       AllowOverride all
       Options -MultiViews
   &amp;lt;/Directory&amp;gt;

   PassengerStartTimeout 60000 # Required since the MiraBox is so slow
   PassengerRuby /usr/local/rvm/wrappers/ruby-1.9.3-p484@gitlab/ruby

   ErrorLog ${APACHE_LOG_DIR}/gitlab_error.log

   # Possible values include: debug, info, notice, warn, error, crit, alert, emerg.
   LogLevel warn
   CustomLog ${APACHE_LOG_DIR}/gitlab_access.log combined
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I removed therubyracer from both the Gemfile and Gemfile.lock, installed Node.js, and deployed all the required gems with Bundler according to the GitLab installation guide. I had everything setup identically to my QEMU-emulated server, and I ran it&amp;hellip; and got a 500 error.&lt;/p&gt;
&lt;p&gt;What the heck? So I started digging into my logs. I actually tail&amp;rsquo;d the GitLab production log, and watched it load. I watched it compile all the assets on the first request (as expected). I saw the request get the layout, and the layout partials&amp;hellip; and then a 500 error after &lt;code&gt;_head.html.haml&lt;/code&gt;. There wasn&amp;rsquo;t actually an error in the GitLab log, or the Apache log, or being shown in Firefox&amp;ndash; just a 500. Normally one actually sees something like &amp;ldquo;function .empty() doesn&amp;rsquo;t exist for Nil.NilClass&amp;rdquo; or something along those lines&amp;hellip; I saw nothing. It was totally bizarre. So I edited _head.html.haml and started old-school debugging: adding print (&lt;code&gt;logger.debug&lt;/code&gt;) statements. Using this technique I discovered that the 500 happened right after &lt;code&gt;application.js&lt;/code&gt; was being included. Interesting&amp;hellip; so it involved the asset pipeline. Just for kicks I tried precompiling the assets with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ bundle exec rake assets:precompile RAILS_ENV&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;production
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And I got an error! I&amp;rsquo;m afraid I don&amp;rsquo;t have the error message saved, but it involved an invalid byte and something about UTF-8. Regardless, it was failing to compile its assets when they were requested, so it barfed a 500 without actually giving any sort of descriptive error (which was annoying).&lt;/p&gt;
&lt;p&gt;What do you do when assets need to be compiled but the server has errors when compiling them? Fortunately I already had the solution in place: Compile it in another, identical environment. I chose the VirtualBox server since it was way faster than the QEMU server. I ran the same precompile line on there, then tar&amp;rsquo;d up the generated assets and placed them in the correct spot on my production server.&lt;/p&gt;
&lt;p&gt;The moment of truth: I restarted everything and it worked beautifully :) .&lt;/p&gt;
&lt;p&gt;GitLab lessons learned:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Unicorn/Puma is not necessary, but not using them requires an altered boot script.&lt;/li&gt;
&lt;li&gt;NginX is not necessary&amp;ndash; Apache2 works great.&lt;/li&gt;
&lt;li&gt;Node.js is a replacement for therubyracer, but it doesn&amp;rsquo;t seem 100% compatible with the ARM architecture. Asset compilation is wonky&amp;hellip; compile elsewhere and deploy.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The only question I have is this: Why did Node.js fail to compile assets on my production server, but succeed to compile assets on my QEMU-emulated ARM server? I have yet to find an answer.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Upgrade makes Mirabox unbootable</title>
      <link>https://kyrofa.com/posts/upgrade-makes-mirabox-unbootable/</link>
      <pubDate>Mon, 29 Jul 2013 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/upgrade-makes-mirabox-unbootable/</guid>
      <description>For some reason (I can&amp;rsquo;t really put a finger on it exactly), I like to keep my servers up-to-date. I know! All those security patches, all those new features&amp;ndash; they&amp;rsquo;re a killer!
In all seriousness, I host several websites on a Mirabox from right behind my TV. I hopped on the plug computing train way back when the SheevaPlug was just a dev kit, and I&amp;rsquo;ve been on it ever since.</description>
      <content:encoded>&lt;p&gt;For some reason (I can&amp;rsquo;t really put a finger on it exactly), I like to keep my servers up-to-date. I know! All those security patches, all those new features&amp;ndash; they&amp;rsquo;re a killer!&lt;/p&gt;
&lt;p&gt;In all seriousness, I host several websites on a &lt;a href=&#34;http://www.globalscaletechnologies.com/p-58-mirabox-development-kit.aspx&#34;&gt;Mirabox&lt;/a&gt; from right behind my TV. I hopped on the plug computing train way back when the SheevaPlug was just a dev kit, and I&amp;rsquo;ve been on it ever since. The reasons are pretty simple: reasonable power and capabilities while using less power than my desktop computer when it&amp;rsquo;s off (i.e. phantom power). I mean come on&amp;ndash; the Mirabox in particular has two gigabit ethernet ports and two USB 3 ports. I have a fantastic RAID 1 setup running on that. Anyway, I digress.&lt;/p&gt;
&lt;p&gt;The Mirabox comes with a custom flash of Debian Squeeze on it that worked just fine. A few weeks ago I decided to get it up-to-date.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get dist-upgrade &lt;span style=&#34;color:#75715e&#34;&gt;# What was I thinking...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And reboot and&amp;hellip; the Mirabox never came back. It just hung when it tried to bring the network interfaces back up. This is the end of the boot log:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;snip&amp;gt;
[ ok ] Setting kernel variables ...done.
[ ok ] Setting up X socket directories... /tmp/.X11-unix /tmp/.ICE-unix.
[FAIL] startpar: service(s) returned failure: module-init-tools ... failed!
pool #0: pkt_size=1536, buf_size=1632 - 2048 of 2048 buffers added
eth0: started
ADDRCONF(NETDEV_UP): eth0: link is not ready
mlan: module license &#39;Marvell Proprietary&#39; taints kernel.
Disabling lock debugging due to kernel taint WLAN FW is active
ADDRCONF(NETDEV_UP): uap0: link is not ready
Bluetooth: Core ver 2.15
NET: Registered protocol family 31
Bluetooth: HCI device and connection manager initialized
Bluetooth: HCI socket layer initialized
Bluetooth: L2CAP ver 2.14
Bluetooth: L2CAP socket layer initialized
Bluetooth: RFCOMM socket layer initialized
Bluetooth: RFCOMM ver 1.11
WLAN FW already running! Skip FW download WLAN FW is active
ADDRCONF(NETDEV_UP): mlan0: link is not ready
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This was, believe it or not, somewhat of a blessing in disguise, since I should NOT have been running the OS off of internal memory. Why, you ask? Because this just happened. There was no way to recover&amp;ndash; I tried booting to different runlevels, just the shell, you name it.&lt;/p&gt;
&lt;p&gt;I ended up rebuilding the server from scratch, but on an SD card this time so I could at the very least back it up (via dd) as I made progress.&lt;/p&gt;
&lt;p&gt;And you know what the issue was? Check out the sources.list:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;deb http://ftp.us.debian.org/debian/ squeeze main contrib non-free
deb http://http.us.debian.org/debian stable main contrib non-free
deb http://security.debian.org squeeze/updates main contrib non-free
deb http://www.backports.org/debian squeeze-backports main contrib non-free
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Does anything jump out at you? Perhaps the fact that the second line is actually the same repository URL, first of all. But that&amp;rsquo;s not the bad part: notice the mixture of squeeze and stable repositories. Stable is now Wheezy! Whichever idiot at Globalscale who set this thing up just potentially screwed all of you by mixing releases. That person has wasted nearly two weeks of my time.&lt;/p&gt;
&lt;p&gt;The solution is simple: delete the second line. The URL was redundant anyway.&lt;/p&gt;
&lt;p&gt;Now you&amp;rsquo;re just using Squeeze!&lt;/p&gt;
&lt;p&gt;You might ask: &amp;ldquo;Well, why not just update to Wheezy?&amp;rdquo; Because the wheezy repos contain the package updates that cause the Mirabox to no longer boot. Which packages, you ask? One of these five (they&amp;rsquo;re all interdependent&amp;hellip; I&amp;rsquo;m not sure which one breaks it):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;initscripts&lt;/li&gt;
&lt;li&gt;netbase&lt;/li&gt;
&lt;li&gt;rsyslog&lt;/li&gt;
&lt;li&gt;wpasupplicant&lt;/li&gt;
&lt;li&gt;ifupdown&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You&amp;rsquo;ll notice in the install logs (if you go ahead with that madness) that &amp;ldquo;netbase breaks ifupdown.&amp;rdquo; I suspect the root cause to be somewhere around there.&lt;/p&gt;
&lt;p&gt;If you figure out how to successfully upgrade to Wheezy, let me know! For now, I&amp;rsquo;m happy with Squeeze.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>QEMU: MiraBox emulation</title>
      <link>https://kyrofa.com/posts/qemu-mirabox-emulation/</link>
      <pubDate>Sat, 20 Jul 2013 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/qemu-mirabox-emulation/</guid>
      <description>As I mentioned in a previous post, I host various websites (including this one in the past) on a MiraBox sitting behind my tv. In that same post I outlined how Globalscale screwed me by mixing releases in the sources.list. I didn&amp;rsquo;t catch it until I had an unbootable system, and I had to completely rebuild my server which took weeks. I very nearly tossed the whole thing in favor of hosting my websites on a &amp;ldquo;real computer, made by people who knew what they were doing,&amp;rdquo; but I reminded myself why I settled on the MiraBox in the first place: I like embedded stuff.</description>
      <content:encoded>&lt;p&gt;As I mentioned in a &lt;a href=&#34;https://kyrofa.com/posts/upgrade-makes-mirabox-unbootable&#34;&gt;previous post&lt;/a&gt;, I host various websites (including this one in the past) on a &lt;a href=&#34;http://www.globalscaletechnologies.com/p-58-mirabox-development-kit.aspx&#34;&gt;MiraBox&lt;/a&gt; sitting behind my tv. In that same post I outlined how Globalscale screwed me by mixing releases in the sources.list. I didn&amp;rsquo;t catch it until I had an unbootable system, and I had to completely rebuild my server which took weeks. I very nearly tossed the whole thing in favor of hosting my websites on a &amp;ldquo;real computer, made by people who knew what they were doing,&amp;rdquo; but I reminded myself why I settled on the MiraBox in the first place: I like embedded stuff. I&amp;rsquo;m a computer engineer, this is what I find interesting. Is installing new things on this box harder than on an x86? Sure. But do you learn new things every time you play with it? Definitely.&lt;/p&gt;
&lt;p&gt;So, I rebuilt my server on the MiraBox again. And now it&amp;rsquo;s working better than ever (now that I&amp;rsquo;ve fixed my sources.list). However, I also have an ownCloud install on here, which I recently tried to upgrade. Of course, I broke it and had to spend a few days rolling back (something weird happens to a PostgreSQL database when you update Owncloud 4.8 to 5&amp;hellip; I haven&amp;rsquo;t figured it out yet). Some of you might be laughing, because I STILL hadn&amp;rsquo;t learned my lesson: &lt;strong&gt;You don&amp;rsquo;t do research and development on production systems&lt;/strong&gt;. You get it working in a parallel environment, and then deploy it. I&amp;rsquo;ve got it now, I promise. However, there&amp;rsquo;s no way I&amp;rsquo;m shelling out for another MiraBox just to play the role of a staging server, so I&amp;rsquo;ve come up with another idea: using QEMU to emulate the MiraBox environment as closely as possible.&lt;/p&gt;
&lt;h1 id=&#34;qemu&#34;&gt;QEMU&lt;/h1&gt;
&lt;p&gt;QEMU is a simple, open-source machine emulator. When I decided to go the virtualization route, my first thought was obviously VirtualBox, but a quick google revealed that it doesn&amp;rsquo;t support other architectures. So after some research, I settled on QEMU for ARM emulation.&lt;/p&gt;
&lt;h1 id=&#34;install-debian&#34;&gt;Install Debian&lt;/h1&gt;
&lt;p&gt;My MiraBox server is running Debian Squeeze, so the first step is to download Debian Squeeze to install on the emulated staging server. However, installing on an ARM is a bit different than installing on, say, an x86. Instead of using the .iso, you need to seperately download the ARMEL kernel image and installer:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ mkdir -p ~/qemu/debian/squeeze
$ mkdir -p ~/qemu/images
$ cd ~/qemu/debian/squeeze
$ wget http://ftp.debian.org/debian/dists/squeeze/main/installer-armel/current/images/versatile/netboot/initrd.gz
$ wget http://ftp.debian.org/debian/dists/squeeze/main/installer-armel/current/images/versatile/netboot/vmlinuz-2.6.32-5-versatile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now an image needs to be created for the VM. I like the QEMU&amp;rsquo;s copy-on-write strategy, so I use the qcow2 format. I also have Debian on a 32 GB SD card inside the MiraBox, so I&amp;rsquo;m making this image 32 GB.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ qemu-img create -f qcow2 server.img 32G
Formatting &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;server.rainveiltech.com.img&amp;#39;&lt;/span&gt;, fmt&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;qcow2 size&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;34359738368&lt;/span&gt; encryption&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;off cluster_size&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;65536&lt;/span&gt; lazy_refcounts&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;off
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now it&amp;rsquo;s time to actually install Debian. I was really hoping for a way to emulate the MiraBox hardware as closely as possible, but it seems that the closest I can get with QEMU is the arm5 instruction set (the MiraBox can use arm7). I figured that would be good enough, so I walked through installing Debian:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ qemu-system-arm -M versatilepb -kernel ../debian/squeeze/vmlinuz-2.6.32-5-versatile -hda server.rainveiltech.com.img -initrd ../debian/squeeze/initrd.gz -append &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;root=/dev/ram&amp;#34;&lt;/span&gt; -m 1G
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After it had finished installing, I fired it up to make sure everything looked good:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ qemu-system-arm -M versatilepb -kernel ../debian/squeeze/vmlinuz-2.6.32-5-versatile -hda server.rainveiltech.com.img -initrd ../kernels/initrd.img-2.6.32-5-versatile -append &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;root=/dev/sda1&amp;#34;&lt;/span&gt; -m 1G
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It looked fine, but by default the user-mode networking stack won&amp;rsquo;t allow any incoming traffic (&lt;a href=&#34;http://en.wikibooks.org/wiki/QEMU/Networking&#34;&gt;reference&lt;/a&gt;) so I decided to just forward port 80 on my host machine to the guest machine, with &lt;code&gt;-redir tcp:&amp;lt;host port&amp;gt;::&amp;lt;guest port&amp;gt;&lt;/code&gt; or in my case:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;$ qemu-system-arm -M versatilepb -kernel ../debian/squeeze/vmlinuz-2.6.32-5-versatile -hda server.rainveiltech.com.img -initrd ../kernels/initrd.img-2.6.32-5-versatile -append &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;root=/dev/sda1&amp;#34;&lt;/span&gt; -m 1G -redir tcp:80::80
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now I can install Apache etc. and test out new settings! I was having some trouble getting Gitlab setup on the MiraBox, so now I can play with it here instead of my production system.&lt;/p&gt;
&lt;p&gt;I still wish I could get Debian running on an emulated arm7 system. If anyone knows how to do that, please share!&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Dell Sputnik beta participant gift</title>
      <link>https://kyrofa.com/posts/dell-sputnik-beta-participant-gift/</link>
      <pubDate>Sun, 06 Jan 2013 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/dell-sputnik-beta-participant-gift/</guid>
      <description>Well, how nice! Look what the UPS man hid behind my garbage can (seriously&amp;hellip; the thing was all damp; it must have been sitting out there for two weeks).
That&amp;rsquo;s pretty nice of them! It even included a note:
The one strange thing is that while it&amp;rsquo;s marked for Project Sputnik (which uses a Dell XPS 13), I&amp;rsquo;m not sure I would feel comfortable carrying such a small computer with it.</description>
      <content:encoded>&lt;p&gt;Well, how nice! Look what the UPS man hid behind my garbage can (seriously&amp;hellip; the thing was all damp; it must have been sitting out there for two weeks).&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s pretty nice of them! It even included a note:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/sputnik-gift-letter.jpg&#34; alt=&#34;gift note&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;The one strange thing is that while it&amp;rsquo;s marked for Project Sputnik (which uses a Dell XPS 13), I&amp;rsquo;m not sure I would feel comfortable carrying such a small computer with it. Indeed, it&amp;rsquo;s better suited for a 17-incher (that&amp;rsquo;s a 17-inch macbook pro in there):&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/sputnik-bag-macbook.jpg&#34; alt=&#34;macbook in bag&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;But it was a nice gesture. Thank you, Dell.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>TSA: &#34;Red is the Bad Color&#34;</title>
      <link>https://kyrofa.com/posts/tsa-red-is-the-bad-color/</link>
      <pubDate>Sat, 08 Dec 2012 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/tsa-red-is-the-bad-color/</guid>
      <description>At work, a large part of my focus is stereo vision. Well, a coworker and I needed to attend a three day training seminar, which we expected would be quite boring. So, we grabbed a pair of cameras, secured their baseline by attaching them to a small metal beam, zip tied the beam to a 12V power supply, and shoved the whole thing into a pelican case so we could continue working on interesting things while we were there.</description>
      <content:encoded>&lt;p&gt;At work, a large part of my focus is stereo vision. Well, a coworker and I needed to attend a three day training seminar, which we expected would be quite boring. So, we grabbed a pair of cameras, secured their baseline by attaching them to a small metal beam, zip tied the beam to a 12V power supply, and shoved the whole thing into a pelican case so we could continue working on interesting things while we were there.&lt;/p&gt;
&lt;p&gt;To the unfamiliar, stereo vision works with two cameras taking frames in sync with each other. In order to facilitate syncing, with these particular cameras, you call one camera the &amp;ldquo;master&amp;rdquo; and the other the &amp;ldquo;slave,&amp;rdquo; having the slave trigger frames when the master says to do so. This requires the cameras being connected in a sync circuit involving a 1.6K resistor. Each camera has a wiring harness included with it, which was used to connect the cameras to the power supply (black and red wires), and the master camera to one end of the 1.6K resister (yellow wire) and the slave camera to the other end of the resister (green wire). The sync circuitry needed to be powered as well, which was another red wire.&lt;/p&gt;
&lt;p&gt;All of these wires were connected by way of an admittedly messy rail, with crescent connectors crimped onto the wires. I mean, come on, we just threw the thing together. The power supply we used was I guess fairly interesting-looking: black, covered in heat sink blades.&lt;/p&gt;
&lt;p&gt;So: We checked the pelican case, and flew to our training with no issues. We messed with stuff for a few days, made no real scientific breakthroughs, and then flew back, checking the case again. When we got back to the lab, we cracked open the box to find a &amp;ldquo;TSA has inspected this&amp;rdquo; tag, and &lt;strong&gt;all of the red wires clipped off&lt;/strong&gt;. I&amp;rsquo;m not kidding. The green wires weren&amp;rsquo;t touched; the yellow wires were fine; the black were also left alone; every &lt;strong&gt;red&lt;/strong&gt; wire was cut.&lt;/p&gt;
&lt;p&gt;Seriously TSA? You were so scared this thing was going to explode that you flashed back to your days watching 24 and decided to cut the red wires? If this was a bomb, do you really thing it would have defused after you started slicing through it?&lt;/p&gt;
&lt;p&gt;Word to the wise: If you check electronics for a flight, make sure your wires aren&amp;rsquo;t red.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Dell Sputnik XPS 13 critiques</title>
      <link>https://kyrofa.com/posts/dell-sputnik-xps-13-critiques/</link>
      <pubDate>Wed, 05 Dec 2012 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/dell-sputnik-xps-13-critiques/</guid>
      <description>As I mentioned in my last post, I recently picked up an XPS 13 at a discount from Dell for the Sputnik beta project. When I say &amp;ldquo;recently,&amp;rdquo; I actually mean about two months ago, which means this is not a &amp;ldquo;first look&amp;rdquo; post. This has been my main computer ever since it arrived, which means I have some thoughts to share. Mostly critiques, at least for this post.
Hardware Design Google pictures of this thing.</description>
      <content:encoded>&lt;p&gt;As I mentioned in my last post, I recently picked up an XPS 13 at a discount from Dell for the Sputnik beta project. When I say &amp;ldquo;recently,&amp;rdquo; I actually mean about two months ago, which means this is not a &amp;ldquo;first look&amp;rdquo; post. This has been my main computer ever since it arrived, which means I have some thoughts to share. Mostly critiques, at least for this post.&lt;/p&gt;
&lt;h2 id=&#34;hardware-design&#34;&gt;Hardware Design&lt;/h2&gt;
&lt;p&gt;Google pictures of this thing. It&amp;rsquo;s really quite pretty. It&amp;rsquo;s also light, and it has a backlit keyboard, which is still difficult to find even though Apple started doing it ages ago. It&amp;rsquo;s also pretty gutsy inside with an i7, while still retaining pretty good battery life (I have not explicitly tested battery performance). The annoying thing is that it only has 4GB of memory. Of course, now that Sputnik is officially released, it has 8. Too bad us beta testers don&amp;rsquo;t get a taste.&lt;/p&gt;
&lt;p&gt;Another detractor from the design is the glossy display. That was the only option. I hate gloss. Who likes it? Who started that idea? Some narcissist who likes staring at his reflection? Give me a matte display any day. The only upside of this is that it&amp;rsquo;s Gorilla glass, which is pretty tough stuff.&lt;/p&gt;
&lt;p&gt;But those few detractors don&amp;rsquo;t hold a candle to this one:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://kyrofa.com/xps13-rear.jpg&#34; alt=&#34;XPS13 intake vents&#34;  /&gt;
&lt;/p&gt;
&lt;p&gt;Notice the vents behind the hinge, kinda like every other computer you&amp;rsquo;ve seen. But this laptop is different&amp;ndash; its intake is on the bottom. Seriously. The fan-powered intake for the vents is right where one would normally place the laptop on one&amp;rsquo;s lap. In fact, if you wear shorts and you have hairy legs (I do), you have to hold this laptop in a very special way not just because the intake fan starts screaming due to starvation, but because it starts grinding on your legs hairs! &lt;strong&gt;Who designed this?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This doesn&amp;rsquo;t even work well sitting on a table! There&amp;rsquo;s simply not enough space between a flat tabletop and the intake to get enough air if you&amp;rsquo;re running full-tilt. Compile a kernel or something and you&amp;rsquo;ll quickly realize the only way to get rid of the screaming fan is to set it teetering off the edge of the table like in the image at the top of this post.&lt;/p&gt;
&lt;p&gt;This is a truly awful design, and would have caused me to pass this laptop up if it weren&amp;rsquo;t for the Sputnik program. However, these complaints relate only to the XPS 13 hardware, so let&amp;rsquo;s see where we stand on the Sputnik software.&lt;/p&gt;
&lt;h2 id=&#34;software&#34;&gt;Software&lt;/h2&gt;
&lt;p&gt;Out of the box (using the Ubuntu 12.04 Sputnik-specific ISO), this thing ran Ubuntu well. The only issue I had was with the touchpad, which worked embarrassingly badly. I hate tap-to-click, and the first thing I always do on a laptop is disable it. I just have big hands, and my palms always cause taps to occur. So: I disabled tap-to-click. But it was only honored for the top 3/4 of the touchpad&amp;ndash; the bottom 1/4 still turned taps into clicks! This driver was incredibly untested. I knew I was using a beta and did what any beta tester should do&amp;ndash; I posted a bug on the &lt;a href=&#34;https://bugs.launchpad.net/dell-sputnik&#34;&gt;Sputnik Launchpad&lt;/a&gt; page and the Sputnik Forums.&lt;/p&gt;
&lt;p&gt;Fast forward two weeks. No activity.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not sure what deal Dell has with Canonical, but Dell does not seem to support this at all from a software standpoint. It seems that they just handed it off to Canonical and/or the community, and expected the &amp;ldquo;beta participants&amp;rdquo; to be their developers. I ended up fixing that bug myself. I don&amp;rsquo;t actually mind so much that Dell isn&amp;rsquo;t messing with it, but I think it&amp;rsquo;s indicative of their motives for this project.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;After working with this platform for a while, it&amp;rsquo;s my belief that if this project succeeds, it will be due to the efforts of &lt;a href=&#34;https://wiki.ubuntu.com/KamalMostafa&#34;&gt;Kamal Mostafa&lt;/a&gt; and the folks at Canonical more than Dell. That said, I do hope it succeeds. I just wish the XPS 13 was a better hardware platform than it is. Fix the ventilation issues, and this platform is a contender. Fix that and the glossy-display-only stuff, and this thing is a winner.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>My new laptop: Dell Sputnik XPS 13</title>
      <link>https://kyrofa.com/posts/my-new-laptop-dell-sputnik-xps-13/</link>
      <pubDate>Sun, 02 Dec 2012 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/posts/my-new-laptop-dell-sputnik-xps-13/</guid>
      <description>My old laptop finally seemed to be on its last legs, so I began shopping. I&amp;rsquo;m the kind of guy who will do research for a month before spending $100 on a hand-held vacuum cleaner, so you can imagine how I deal with something having three or more zeros in its price tag.
I started using Debian when I was just a kid, and moved to Ubuntu a few years later.</description>
      <content:encoded>&lt;p&gt;My old laptop finally seemed to be on its last legs, so I began shopping. I&amp;rsquo;m the kind of guy who will do research for a month before spending $100 on a hand-held vacuum cleaner, so you can imagine how I deal with something having three or more zeros in its price tag.&lt;/p&gt;
&lt;p&gt;I started using Debian when I was just a kid, and moved to Ubuntu a few years later. I was hoping that I could purchase a laptop in support of Linux&amp;ndash; maybe System76, etc. But my artsy side shied away from every Linux-specific laptop I could find. I wouldn&amp;rsquo;t call them ugly, but they&amp;rsquo;re not at all appealing! I would like one that doesn&amp;rsquo;t look bullet-proof, please.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s when I happened upon Project Sputnik currently in beta status at Dell. I thought &amp;ldquo;Oh, this is perfect! My current laptop will probably last until the Fall of 2012, when the Sputnik project will be official.&amp;rdquo; So, I decided to wait while I researched the XPS 13&amp;rsquo;s hardware. But I also decided to post a comment on Barton George&amp;rsquo;s blog (the guy running Sputnik), and ask if they had a more specific release date than &amp;ldquo;Fall 2012.&amp;rdquo; Rather than reply to that question, he sent me a response saying there was still room in the beta if I wanted in. I&amp;rsquo;d get 20% off and by the way that code expires in one week.&lt;/p&gt;
&lt;p&gt;20% off in trade for having zero time to research the XPS 13. But it&amp;rsquo;s supporting Linux! Okay&amp;hellip; I did it.&lt;/p&gt;
&lt;p&gt;Was it worth it? I&amp;rsquo;ll post a review shortly.&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Archive</title>
      <link>https://kyrofa.com/archives/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/archives/</guid>
      <description>archives</description>
      <content:encoded></content:encoded>
    </item>
    
    <item>
      <title>Search</title>
      <link>https://kyrofa.com/search/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/search/</guid>
      <description></description>
      <content:encoded></content:encoded>
    </item>
    
    <item>
      <title>Who is Kyle Fazzari?</title>
      <link>https://kyrofa.com/about/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://kyrofa.com/about/</guid>
      <description>About Kyle Fazzari</description>
      <content:encoded>&lt;p&gt;Kyle is a husband, father of seven, roboticist, and a prolific open source software developer (where he goes by the handle &amp;ldquo;kyrofa&amp;rdquo;). He maintains and contributes to a number of projects, including Ubuntu, Nextcloud, versions 1 and 2 of the Robot Operating System (ROS), as well as the snapcraft CLI and snapd, two key technologies behind snaps and Ubuntu Core.&lt;/p&gt;
&lt;p&gt;Kyle&amp;rsquo;s day job is at &lt;a href=&#34;https://www.miriamtech.com/&#34;&gt;Miriam Technologies&lt;/a&gt;, a software consulting and contracting company, where he works on everything from cloud infrastructure and security to industrial real-time product sorting machines. In the past he&amp;rsquo;s worked as a staff engineer on the security and robotics teams for &lt;a href=&#34;https://canonical.com/&#34;&gt;Canonical&lt;/a&gt; (the company that publishes &lt;a href=&#34;https://ubuntu.com/&#34;&gt;Ubuntu&lt;/a&gt;), and as a lead roboticist for the US Department of Defense.&lt;/p&gt;
&lt;p&gt;He believes that a maintained and stable API is a work of art, and is easily bothered when they aren&amp;rsquo;t. He thinks C++ is the only real language, and will never admit a begrudging fondness for Ruby and Python. He tends to publish blog posts about anything technical that interests him.&lt;/p&gt;
</content:encoded>
    </item>
    
  </channel>
</rss>
