<?xml version="1.0" encoding="UTF-8"?>
<rss version='2.0' xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>YunoJuno Tech Team</title>
    <description>Making Freelance Work</description>
    <link>http://tech.yunojuno.com/feed</link>
    <atom:link href="http://tech.yunojuno.com/feed" rel="self" type="application/rss+xml"/>
    <category domain="tech.yunojuno.com">Content Management/Blog</category>
    <language>en-us</language>
      <pubDate>Fri, 25 Mar 2022 12:36:35 +0000</pubDate>
    <managingEditor>tech@yunojuno.com (YunoJuno Tech Team)</managingEditor>
      <item>
        <guid>http://tech.yunojuno.com/2022-blog-reboot#53353</guid>
          <pubDate>Fri, 25 Mar 2022 12:36:35 +0000</pubDate>
        <link>http://tech.yunojuno.com/2022-blog-reboot</link>
        <title>2022 Blog Reboot</title>
        <description></description>
        <content:encoded><![CDATA[<p>After several years of silence we&#39;re firing up the blog engines again.<br>
In order to give the reboot some structure around what to write about,<br>
I&#39;m going to write a post on each of the public OSS projects that we<br>
currently maintain and use in our live platform. There are 27 that we<br>
maintain, and a further 6 that we don&#39;t, but have contributed to in some<br>
small way, so there&#39;s plenty to write about, but before getting into<br>
them, this post is going to be about why we write OSS in the first<br>
place.</p>

<p>All of the packages listed below provide some value to our platform -<br>
and the majority started out life as internal functions / modules that<br>
we wrote as part of our daily work. At some point in time they became<br>
sufficiently generic / non-specific that they became candidates for<br>
releasing publicly. The reason for doing so is principally because it<br>
forces the developer to think about the problem they are solving in a<br>
more rounded way. The solutions that come out of treating a problem as a<br>
piece of drop-in functionality are nearly always cleaner and clearer<br>
than the equivalent code placed in the middle of a 1,000LOC module.</p>

<p>An additional benefit is that forcing the developer to acknowledge that<br>
the code is going to be public subconsciously makes them try a little<br>
bit harder - comments are clearer, code is tidier, tests more complete.<br>
An old trick when reviewing code with junior developers was to ask how<br>
they&#39;d feel if it was presented as example code in 128pt font to an<br>
audience of their peers at a conference. Writing code &#39;in public&#39; has<br>
the same effect.</p>

<p>A final benefit is just that it&#39;s easier to read / reason code that is<br>
isolated from the rest of the behemoth that you are building elsewhere.<br>
Most of the packages are small and simple - a small suite of tests has a<br>
large degree of coverage, and they all come with a README that explains<br>
how it works, and what problem it solves.</p>

<p>That&#39;s enough preamble - this is the list we&#39;ll go through in due course:</p>
<div class="highlight"><pre><span></span><span class="n">django</span><span class="o">-</span><span class="n">appmail</span>           <span class="c1"># Django app for managing localised email templates.</span>
<span class="n">django</span><span class="o">-</span><span class="n">charid</span><span class="o">-</span><span class="n">field</span>      <span class="c1"># Provides a char-based, prefixable ID field for your Django models. Supports cuid, ksuid, ulid, et al.</span>
<span class="n">django</span><span class="o">-</span><span class="n">command</span><span class="o">-</span><span class="n">log</span>       <span class="c1"># Django management command auditing app</span>
<span class="n">django</span><span class="o">-</span><span class="n">countries</span>         <span class="c1"># Provides a country field for Django models.</span>
<span class="n">django</span><span class="o">-</span><span class="n">csv</span><span class="o">-</span><span class="n">downloads</span>     <span class="c1"># Django app for enabling and tracking CSV downloads</span>
<span class="n">django</span><span class="o">-</span><span class="n">frozen</span><span class="o">-</span><span class="n">field</span>      <span class="c1"># Django model field used to store snapshot of data.</span>
<span class="n">django</span><span class="o">-</span><span class="n">geoip2</span><span class="o">-</span><span class="n">extras</span>     <span class="c1"># Additional functionality using the GeoIP2 database.</span>
<span class="n">django</span><span class="o">-</span><span class="n">magic</span><span class="o">-</span><span class="n">link</span>        <span class="c1"># Django app for managing tokenised &#39;magic link&#39; logins.</span>
<span class="n">django</span><span class="o">-</span><span class="n">nps</span>               <span class="c1"># Django app supporting Net Promoter Score (NPS) surveys.</span>
<span class="n">django</span><span class="o">-</span><span class="n">onfido</span>            <span class="c1"># Django app for integration with Onfido.</span>
<span class="n">django</span><span class="o">-</span><span class="n">perimeter</span>         <span class="c1"># Site-wide perimeter access control for Django projects.</span>
<span class="n">django</span><span class="o">-</span><span class="n">project</span><span class="o">-</span><span class="n">checks</span>    <span class="c1"># Django management commands used to output useful project information.</span>
<span class="n">django</span><span class="o">-</span><span class="n">request</span><span class="o">-</span><span class="n">logger</span><span class="o">-</span><span class="mi">2</span>  <span class="c1"># Django model for storing HttpRequest information.</span>
<span class="n">django</span><span class="o">-</span><span class="n">request</span><span class="o">-</span><span class="n">profiler</span>  <span class="c1"># A simple Django project profiler for timing HTTP requests.</span>
<span class="n">django</span><span class="o">-</span><span class="n">request</span><span class="o">-</span><span class="n">token</span>     <span class="c1"># JWT-backed Django app for managing querystring tokens.</span>
<span class="n">django</span><span class="o">-</span><span class="n">s3</span><span class="o">-</span><span class="n">upload</span>         <span class="c1"># Integrates direct client-side uploading to s3 with Django.</span>
<span class="n">django</span><span class="o">-</span><span class="n">side</span><span class="o">-</span><span class="n">effects</span>      <span class="c1"># Django app for managing external side effects.</span>
<span class="n">django</span><span class="o">-</span><span class="n">stripe</span><span class="o">-</span><span class="n">lite</span>       <span class="c1"># A library to aid Django integration with Stripe.</span>
<span class="n">django</span><span class="o">-</span><span class="n">user</span><span class="o">-</span><span class="n">visit</span>        <span class="c1"># Django app used to track user visits.</span>
<span class="n">django</span><span class="o">-</span><span class="n">utm</span><span class="o">-</span><span class="n">tracker</span>       <span class="c1"># Django app for extracting and storing UTM tracking values.</span>
<span class="n">django</span><span class="o">-</span><span class="n">visitor</span><span class="o">-</span><span class="k">pass</span>      <span class="c1"># Django app for managing temporary session-based users.</span>
<span class="n">django</span><span class="o">-</span><span class="n">zapier</span><span class="o">-</span><span class="n">triggers</span>   <span class="c1"># Simple Django app for managing Zapier triggers.</span>
<span class="n">elasticsearch</span><span class="o">-</span><span class="n">django</span>     <span class="c1"># Elasticsearch Django app.</span>
<span class="n">python</span><span class="o">-</span><span class="n">env</span><span class="o">-</span><span class="n">utils</span>         <span class="c1"># Utility functions to make it easier to work with os.environ</span>
</pre></div>]]></content:encoded>
      </item>
      <item>
        <guid>http://tech.yunojuno.com/automating-github-with-glitch#38018</guid>
          <pubDate>Sun, 01 Apr 2018 15:19:13 +0100</pubDate>
        <link>http://tech.yunojuno.com/automating-github-with-glitch</link>
        <title>Automating Github with Glitch</title>
        <description>Customising your build process</description>
        <content:encoded><![CDATA[<blockquote>
<p>Here&#39;s the story: After a year in beta, @Glitch opens up today, ready for you to build and launch real, live apps. And the app that runs <a href="http://glitch.com">http://glitch.com</a>  itself is now open source, so you can directly help shape Glitch&#39;s features and future.<br>
@anildash</p>
</blockquote>

<p>On this, the day after <a href="">@anildash</a>&#39;s announcement, I thought I&#39;d share how we&#39;ve been using Glitch to automate Github, and personalise our own team workflow.</p>

<p><strong>tl;dr</strong> <a href="https://glitch.me">Glitch</a> is a great webhook platform - it&#39;s like <a href="https://ifttt.com">IFTTT</a> for developers, and when combined with Github&#39;s API can automate almost any git workflow you can think of.</p>

<hr>

<p>We use <a href="http://nvie.com/posts/a-successful-git-branching-model/">gitflow</a> (with some modifications) as our git workflow in combination with Github pull requests, and Jira for tickets. Whilst this combination works well enough, without any automation there are a number of steps that require manual intervention, and are prone to errors:</p>

<ol>
<li><p>Incorrect merges - with gitflow a hotfix should be branched from and merged into master (as well as dev, more on that later) - it&#39;s easy to forget;</p></li>
<li><p>Pull request titles - by convention we prefix PR titles with the Jira ticket number - <code>YJP-123: Implement feature foo</code>; </p></li>
<li><p>Labels - we like to label hotfix PRs;</p></li>
<li><p>Reviews - we insist on two approvals per PR - something that GH does not natively support.</p></li>
</ol>

<p>Using Glitch as a webhook handler, we have now automated all of the above. We have a series of Github status checks (like the CI build checks you see) that enforce all of the rules, flag any errors, and apply labels. In addition, it will automatically open and maintain a Release PR, so that we can always see what is ready to deploy. I built the initial handler myself, starting from a JS knowledge of (effectively) zero - and it was a great developer experience. (Pls don&#39;t judge me on the code - as I said, I don&#39;t really know anything about JS.)</p>

<p>This is the handler that enforces two reviews:</p>
<div class="highlight"><pre><span></span><span class="c1">// Check the number of approvals on the PR, and respond with an appropriate</span>
<span class="c1">// status (&#39;pending&#39; if not enough, &#39;success&#39; if there are) and message.</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s1">&#39;/approval_check&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>

  <span class="kd">let</span> <span class="nx">status</span><span class="p">;</span>
  <span class="kd">let</span> <span class="nx">pr</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">pull_request</span><span class="p">;</span>

  <span class="k">if</span> <span class="p">(</span><span class="nx">utils</span><span class="p">.</span><span class="nx">isRelease</span><span class="p">(</span><span class="nx">pr</span><span class="p">)){</span>
    <span class="nx">status</span> <span class="o">=</span> <span class="s2">&quot;No approvals required for a release&quot;</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="p">{</span>
    <span class="nx">github</span><span class="p">.</span><span class="nx">countApprovals</span><span class="p">(</span><span class="nx">pr</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="nx">approval_count</span> <span class="p">=&gt;</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">approval_count</span> <span class="o">===</span> <span class="mi">0</span><span class="p">){</span>
        <span class="nx">status</span> <span class="o">=</span> <span class="nx">github</span><span class="p">.</span><span class="nx">sendStatusUpdate</span><span class="p">(</span><span class="nx">pr</span><span class="p">,</span> <span class="s2">&quot;approvals&quot;</span><span class="p">,</span> <span class="s2">&quot;pending&quot;</span><span class="p">,</span> <span class="s2">&quot;Requires at least two approvals&quot;</span><span class="p">);</span>
      <span class="p">}</span>
      <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">approval_count</span> <span class="o">===</span> <span class="mi">1</span><span class="p">){</span>
        <span class="nx">status</span> <span class="o">=</span> <span class="nx">github</span><span class="p">.</span><span class="nx">sendStatusUpdate</span><span class="p">(</span><span class="nx">pr</span><span class="p">,</span> <span class="s2">&quot;approvals&quot;</span><span class="p">,</span> <span class="s2">&quot;pending&quot;</span><span class="p">,</span> <span class="s2">&quot;Requires one more approval&quot;</span><span class="p">);</span>
      <span class="p">}</span>
      <span class="k">else</span> <span class="p">{</span>
        <span class="nx">status</span> <span class="o">=</span> <span class="nx">github</span><span class="p">.</span><span class="nx">sendStatusUpdate</span><span class="p">(</span><span class="nx">pr</span><span class="p">,</span> <span class="s2">&quot;approvals&quot;</span><span class="p">,</span> <span class="s2">&quot;success&quot;</span><span class="p">,</span> <span class="nx">approval_count</span> <span class="o">+</span> <span class="s2">&quot; approvals received&quot;</span><span class="p">);</span>
      <span class="p">}</span>
    <span class="p">});</span>
  <span class="p">}</span>
  <span class="nx">res</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">status</span><span class="p">);</span>
<span class="p">});</span>
</pre></div>
<p>The Github call to update the status is:</p>
<div class="highlight"><pre><span></span><span class="c1">// send the PR status check update</span>
<span class="nx">sendStatusUpdate</span> <span class="o">=</span> <span class="nx">async</span> <span class="kd">function</span><span class="p">(</span><span class="nx">pull_request</span><span class="p">,</span> <span class="nx">context</span><span class="p">,</span> <span class="nx">status</span><span class="p">,</span> <span class="nx">description</span><span class="p">){</span>
  <span class="kd">let</span> <span class="nx">retval</span> <span class="o">=</span> <span class="nx">status</span> <span class="o">+</span> <span class="s2">&quot; on &#39;&quot;</span> <span class="o">+</span> <span class="nx">pull_request</span><span class="p">.</span><span class="nx">title</span> <span class="o">+</span> <span class="s2">&quot;&#39;: &quot;</span> <span class="o">+</span> <span class="nx">description</span><span class="p">;</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">retval</span><span class="p">);</span>
  <span class="c1">// https://octokit.github.io/rest.js/#api-Repos-createStatus</span>
  <span class="kd">let</span> <span class="nx">response</span> <span class="o">=</span> <span class="nx">await</span> <span class="nx">octokit</span><span class="p">.</span><span class="nx">repos</span><span class="p">.</span><span class="nx">createStatus</span><span class="p">({</span>
    <span class="nx">owner</span><span class="o">:</span> <span class="nx">pull_request</span><span class="p">.</span><span class="nx">head</span><span class="p">.</span><span class="nx">repo</span><span class="p">.</span><span class="nx">owner</span><span class="p">.</span><span class="nx">login</span><span class="p">,</span>
    <span class="nx">repo</span><span class="o">:</span> <span class="nx">pull_request</span><span class="p">.</span><span class="nx">head</span><span class="p">.</span><span class="nx">repo</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span>
    <span class="nx">sha</span><span class="o">:</span> <span class="nx">pull_request</span><span class="p">.</span><span class="nx">head</span><span class="p">.</span><span class="nx">sha</span><span class="p">,</span>
    <span class="nx">state</span><span class="o">:</span> <span class="nx">status</span><span class="p">,</span>
    <span class="nx">description</span><span class="o">:</span> <span class="nx">description</span><span class="p">,</span>
    <span class="nx">context</span><span class="o">:</span> <span class="s2">&quot;Junobot (&quot;</span> <span class="o">+</span> <span class="nx">context</span> <span class="o">+</span> <span class="s2">&quot;)&quot;</span>
  <span class="p">});</span>
  <span class="k">return</span> <span class="nx">retval</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<p>And the output looks like this:</p>

<p><img alt="Silvrback blog image " src="https://silvrback.s3.amazonaws.com/uploads/d95107b9-9db0-4a8c-882e-1fdc957f3694/Screenshot%202018-04-01%2014.44.38.png" /></p>

<p>In addition, we can now automatically create a Release PR (dev to master), so that we always have an open release ready to merge / deploy - that details the list of all the constituent Jira tickets, and also has the labels from all the merged branches applied, so we can see if a release requires database migrations, feature flags, etc.</p>
<div class="highlight"><pre><span></span><span class="c1">// If this is a merge event, then update the release PR.</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">req</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">action</span> <span class="o">===</span> <span class="s2">&quot;closed&quot;</span> <span class="o">&amp;&amp;</span> <span class="nx">pr</span><span class="p">.</span><span class="nx">merged_at</span><span class="p">){</span>
  <span class="nx">github</span><span class="p">.</span><span class="nx">getRelease</span><span class="p">(</span><span class="nx">pr</span><span class="p">.</span><span class="nx">head</span><span class="p">.</span><span class="nx">repo</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="nx">release</span> <span class="p">=&gt;</span> <span class="p">{</span>
    <span class="c1">// NB this will add all labels, so if you still</span>
    <span class="c1">// have WIP or DNM, these will be added to the release!</span>
    <span class="nx">github</span><span class="p">.</span><span class="nx">addLabels</span><span class="p">(</span><span class="nx">release</span><span class="p">,</span> <span class="nx">github</span><span class="p">.</span><span class="nx">getLabels</span><span class="p">(</span><span class="nx">pr</span><span class="p">));</span>
  <span class="p">});</span>
<span class="p">}</span>
</pre></div>
<p>All of this is created automatically on our behalf:</p>

<p><img alt="Silvrback blog image " src="https://silvrback.s3.amazonaws.com/uploads/d60401a1-eb01-4322-a2e7-9b70a99625e0/Screenshot%202018-04-01%2014.51.53.png" /></p>

<p>Emboldened by the relative success of the above, I decided to have a go at automating the merge itself - we like to <code>squash</code> feature and hotfix branch merges, so that we have a clean <strong>dev</strong>, and devs can easily forget this. In addition, with gitflow it is necessary to merge a hotfix into master and dev at the same time. This is not something that Github supports, and is the one step in the process that devs still complete locally, rather than through the GH interface. Automating this completes the process.</p>
<div class="highlight"><pre><span></span><span class="c1">// Merge a numbered pull request, using a specific method</span>
<span class="c1">// (&#39;merge&#39;, &#39;squash&#39;), and then merge back into dev if</span>
<span class="c1">// this is a hotfix</span>
<span class="kr">const</span> <span class="nx">mergePullRequest</span> <span class="o">=</span> <span class="nx">async</span> <span class="p">(</span><span class="nx">pull_request</span><span class="p">,</span> <span class="nx">merge_method</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
  <span class="kd">let</span> <span class="nx">repo</span> <span class="o">=</span> <span class="nx">pull_request</span><span class="p">.</span><span class="nx">base</span><span class="p">.</span><span class="nx">repo</span><span class="p">;</span>
  <span class="k">return</span> <span class="nx">octokit</span><span class="p">.</span><span class="nx">pullRequests</span><span class="p">.</span><span class="nx">merge</span><span class="p">({</span>
    <span class="nx">owner</span><span class="o">:</span> <span class="nx">repo</span><span class="p">.</span><span class="nx">owner</span><span class="p">.</span><span class="nx">login</span><span class="p">,</span>
    <span class="nx">repo</span><span class="o">:</span> <span class="nx">repo</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span>
    <span class="nx">number</span><span class="o">:</span> <span class="nx">pull_request</span><span class="p">.</span><span class="nx">number</span><span class="p">,</span>
    <span class="nx">merge_method</span><span class="o">:</span> <span class="nx">merge_method</span><span class="p">,</span>
  <span class="p">}).</span><span class="nx">then</span><span class="p">(</span><span class="nx">resp</span> <span class="p">=&gt;</span> <span class="p">{</span> 
    <span class="c1">// NB assuming that it merged OK     </span>
    <span class="k">return</span> <span class="nx">resp</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">sha</span><span class="p">;</span>
  <span class="p">}).</span><span class="nx">then</span><span class="p">(</span><span class="nx">sha</span> <span class="p">=&gt;</span> <span class="p">{</span> 
    <span class="k">if</span> <span class="p">(</span><span class="nx">utils</span><span class="p">.</span><span class="nx">isHotfix</span><span class="p">(</span><span class="nx">pull_request</span><span class="p">)){</span>
      <span class="nx">mergeBranch</span><span class="p">(</span><span class="nx">repo</span><span class="p">,</span> <span class="s2">&quot;dev&quot;</span><span class="p">,</span> <span class="nx">sha</span><span class="p">,</span> <span class="s2">&quot;Merge hotfix: &quot;</span> <span class="o">+</span> <span class="nx">pull_request</span><span class="p">.</span><span class="nx">title</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="s2">&quot;OK&quot;</span><span class="p">;</span>
  <span class="p">}).</span><span class="k">catch</span><span class="p">(</span><span class="nx">error</span> <span class="p">=&gt;</span> <span class="p">{</span>
    <span class="k">return</span> <span class="s2">&quot;Error&quot;</span><span class="p">;</span>
  <span class="p">});</span>
<span class="p">};</span>
</pre></div>
<p>This update to Junobot has also involved a sinister change in personality:</p>

<p><img alt="Silvrback blog image " src="https://silvrback.s3.amazonaws.com/uploads/c991db57-c169-47d7-8131-cc510695c675/Screenshot%202018-04-01%2014.04.13.png" /></p>

<p>We now have no option but to comply. I for one welcome our robot overlords.</p>

<p><img alt="Silvrback blog image " src="https://silvrback.s3.amazonaws.com/uploads/4062df1e-69c7-4da2-b021-6241ecda4503/Screenshot%202018-04-01%2012.14.29.png" /></p>

<p>The merging webhook is available for remixing here: <a href="https://glitch.com/edit/#!/bog-rifle">https://glitch.com/edit/#!/bog-rifle</a></p>
]]></content:encoded>
      </item>
      <item>
        <guid>http://tech.yunojuno.com/packaging-python3-aws-lambda-functions#34748</guid>
          <pubDate>Thu, 05 Oct 2017 15:39:54 +0100</pubDate>
        <link>http://tech.yunojuno.com/packaging-python3-aws-lambda-functions</link>
        <title>Packaging Python3 AWS Lambda Functions</title>
        <description></description>
        <content:encoded><![CDATA[<p>This is part one of a two part post on how to set up transfer data from Redshift to an SFTP server, using S3 as transient storage, and Lambda as the processing function. Part two will deal with the S3/Lambda configuration and permissions (IAM). This first part deals with how to package and deploy a Lambda expression, written in Python3, with some complex platform-specific dependencies (crypto).</p>

<h1 id="background">Background</h1>

<p>AWS Lambda is Amazon&#39;s &#39;serverless&#39; or Function-as-a-Service, product. They were first out of the gate, but Google has Cloud Functions, and Microsoft has Azure Functions, so everyone&#39;s at it - they are officially a thing. The concept is pretty simple - written a small single-purpose function (in the language of your choice - they all support a broad range of environments) - deploy it to the relevant platform, and configure it to fire on a specified event.</p>

<p>We chose AWS because it suited out purpose best - we had a requirement to push a data extract from Amazon Redshift (our data warehouse) to a remote SFTP server, and the recommended method for extracting Redshift data is to use the <code>UNLOAD</code> command which will publish an extract file to S3. The bit we had to write was how to get the file from S3 to the SFTP server, and to do this on receipt of the extract. It&#39;s the perfect Lambda scenario. All we had to do was write the function, and deploy it.</p>

<h1 id="packaging-python3-functions">Packaging Python3 Functions</h1>

<h3 id="1-write-a-python-function">1. Write a python function</h3>

<p>At its simplest, a Lambda function is literally just that - a function. We chose Python as our runtime environment, so all we had to do was write a function with the correct signature:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">my_lambda_function</span><span class="p">(</span><span class="n">event</span><span class="p">,</span> <span class="n">context</span><span class="p">):</span>
    <span class="k">print</span><span class="p">(</span><span class="s2">&quot;Hello, world&quot;</span><span class="p">)</span>
</pre></div>
<p>If the function were this simple, you could write it directly into the Lambda console, however most functions will rely on some external dependencies, and if that&#39;s the case, you&#39;ll need to create a function package.</p>

<h3 id="2-add-dependencies">2. Add dependencies</h3>

<p>To package up a function that has external dependencies, you need to create a zip archive that contains the dependencies and your function module. The Amazon docs are pretty good on how to do this (<a href="http://docs.aws.amazon.com/lambda/latest/dg/lambda-python-how-to-create-deployment-package.html">http://docs.aws.amazon.com/lambda/latest/dg/lambda-python-how-to-create-deployment-package.html</a>) - and if you follow them through you&#39;ll have a <code>package.zip</code> to upload in no time. The following <code>Makefile</code> will do this for you:</p>
<div class="highlight"><pre><span></span><span class="nf">package</span><span class="o">:</span>
<span class="c">  # install dependencies into package directory</span>
  pip install -r requirements.txt -t package
<span class="c">  # copy in your lambda .py source file(s)</span>
  cp my_script.py package/
<span class="c">  # zip up the entire directory into package.zip</span>
  <span class="nb">cd</span> package<span class="p">;</span> zip -r ../package.zip .
<span class="c">  # always clean up after yourself</span>
  rm -rf package/
</pre></div>
<p>This works very well so long as your dependencies have no C extensions or platform-specific installs - if you do (e.g. image processing, crypto, etc.), then you&#39;ll find the function will run fine locally, but fail when uploaded to AWS, because AWS isn&#39;t running macOS (/Ubuntu/Windows/...) - it&#39;s running <strong>amazonlinux</strong>.</p>

<h3 id="3-adding-platform-specific-dependencies">3. Adding platform-specific dependencies</h3>

<p>In order to create a package that contains the right packages for amazonlinux, you need to run the <code>pip install</code> from within an amazonlinux environment. Fortunately Amazon make available a range of Docker containers to make this pretty easy. Unfortunately, the default image does not come in a Python3 variant, so you&#39;ll need to install that (as well as <code>zip</code>).</p>

<h3 id="4-add-python3-support">4. Add Python3 support</h3>

<p>Although the docs will tell you that there isn&#39;t any formal support for Python3 <em>yet</em>, it turns out that there is a distribution available, and installing it is pretty simple. This Dockerfile will install python3 and zip (so we can zip up the package):</p>
<div class="highlight"><pre><span></span>FROM amazonlinux:latest
<span class="c1"># Install python3.6 and zip</span>
RUN yum install -y python36 zip 

<span class="c1"># This is the mount location for the Lambda function directory</span>
VOLUME <span class="o">[</span><span class="s2">&quot;/lambda&quot;</span><span class="o">]</span>
WORKDIR <span class="s2">&quot;/lambda&quot;</span>

<span class="c1"># Default entrypoint / command is to package the function</span>
ENTRYPOINT <span class="o">[</span><span class="s2">&quot;make&quot;</span><span class="o">]</span>
CMD <span class="o">[</span><span class="s2">&quot;package&quot;</span><span class="o">]</span>
</pre></div>
<p>Using this Dockerfile you can build an image that can be used to package up your application for deployment to Lambda. You need to run make command from within the container, thus:</p>
<div class="highlight"><pre><span></span><span class="nb">cd</span> path/to/project
<span class="c1"># build the new image</span>
docker build -t lambda-packager .
<span class="c1"># run a single-use container off the image to create the package</span>
docker run --volume <span class="k">$(</span><span class="nb">pwd</span><span class="k">)</span>:/lambda lambda-packager
</pre></div>
<p>This <em>should</em> produce a single <code>package.zip</code> archive that contains your script together with all the dependencies required, built for the amazonlinux environment. Upload this to AWS, and test it out.</p>
]]></content:encoded>
      </item>
  </channel>
</rss>