michaelheap.comThoughts on leadership, code and how to fix odd edge cases in tools (not necessarily in that order)2023-03-20T11:00:17Zhttps://michaelheap.comMichael Heapm@michaelheap.comThe TAPE Model for Content2023-03-20T11:00:17Zhttps://michaelheap.com/tape-method-content/<p>Content is a mainstay of many Developer Relations teams, but it can sometimes be tough to show the impact that you’re having. Content on your company blog can be instrumented and attributed well, but what about things like YouTube videos or recorded conference talks?</p>
<p>You can build out complex tracking systems with UTM parameters and short URLs, but there’s an easier way to understand how your content is being used. It all starts with your internal teams.</p>
<p>DevRel is typically a cost centre. There isn’t any revenue attributed directly to the team. The best way I’ve found to justify your existence (and yes, unfortunately this is a thing in every business) is to attach yourself to the revenue generating teams.</p>
<p>Serving your internal audience has two outcomes:</p>
<ol>
<li>You support teams that drive new business. The sales team can link to your videos to show prospective customers the capabilities of your platform and how easy it is to use</li>
<li>You save time for existing teams, which means you’re saving $$$. Every proof of concept cycle that is 10% faster thanks to your demo videos is money that the business isn’t spending on Sales Engineers. Every support ticket that is replied to with a link to your content is time that an agent doesn’t have to spend writing a response.</li>
</ol>
<p>In both of these cases you’re drawing a direct link between the content that you’re producing and the impact it has on the revenue or efficiency of the business.</p>
<p>This is where the TAPE model comes in.</p>
<p>TAPE stands for <strong>T</strong>rigger, <strong>A</strong>ction, <strong>P</strong>eople, <strong>E</strong>xamples.</p>
<ul>
<li><strong>Trigger</strong>: <em>Why</em> are we producing this content? What problems have we observed that we can help solve?</li>
<li><strong>Action</strong>: <em>What</em> are we producing? Is this best presented in the documentation, as a blog post, as a video etc?</li>
<li><strong>People</strong>: <em>Who</em> is asking for this? Not our end customers, but our internal stakeholders. Can we make other teams successful?</li>
<li><strong>Examples</strong>: <em>Where</em> is this being used? Is your content being used to solve support tickets? Is it being used to prove we have certain capabilities during an RFP?</li>
</ul>
<p>By matching the content you produce to pain points that internal teams are having you <em>know</em> that the content is going to resonate with at least one audience. This is your <strong>trigger</strong>, your reason for producing the content in the first place.</p>
<p>Understanding who’s going to consume your content helps you decide which <strong>action</strong> to take. A VP of infrastructure is more likely to scan a documentation page than watch a video. A developer at a Global System Integrator might be more at home with a 60 minute deep dive video on to how to write plugins for your system.</p>
<p>Aim to make friends by supporting the right <strong>people</strong>. If you’re working with the sales team, target enterprise sales reps rather than lower value digital customers. If you’re working with Sales Engineers, work with regional leads who can help evangelise the work you do more widely. Saying “I helped $VP with $TOP10 customer” is much more impactful than saying “I solved 10 loosely related support tickets”</p>
<p>Then finally, keep track of how your content is being used. Make a note every time someone links to one of your pieces of content in Slack. This could be a product manager asking how a feature built before they joined works. It could be someone from marketing asking how a new feature behaves so that they can position it correctly. Building up a list of <strong>examples</strong> allows you to show that not only did you identify an opportunity to support people internally, the content is being used repeatedly to help enable the rest of the business.</p>
<h2 id="examples" tabindex="-1">Examples</h2>
<p>Alright, so what does that actually look like? Here are five examples to get you started.</p>
<p><strong>Trigger:</strong> Product decision to deprecate a feature<br />
<strong>Action:</strong> FAQ, migration recommendations<br />
<strong>People:</strong> CPO, Support, Docs Team, Customer Success<br />
<strong>Examples</strong>: CPO shares with internal teams. Support use with inbound tickets. Docs team write public facing docs. Customer Success proactively reach out to their customers to explain how to migrate.</p>
<p><strong>Trigger:</strong> New feature released<br />
<strong>Action:</strong> 3 minute demo video showing how it works<br />
<strong>People:</strong> Product Manager, Product Marketing, Social Media Manager, Solutions Engineers, Sales<br />
<strong>Examples</strong>: Product Manager / Product Marketing used in sales enablement. Social media used for a blog post. Solutions engineers used as a basis for their own demos. Sales used to show prospects the platform capabilities.</p>
<p><strong>Trigger:</strong> Marketing campaign by competitor<br />
<strong>Action:</strong> Rebuttal video showing off product capabilities<br />
<strong>People:</strong> Product Marketing, Sales<br />
<strong>Examples</strong>: Product Marketing builds battle cards using the information. Sales use the content to influence champions within a business.</p>
<p><strong>Trigger:</strong> Difficult to run your OSS project on MacOS<br />
<strong>Action:</strong> Work on README and improving error messages in the app<br />
<strong>People:</strong> Engineering, Community, Support<br />
<strong>Examples</strong>: Increased contribution velocity for engineering. A community develops around your project. Reduced support ticket volume for common use cases</p>
<p><strong>Trigger:</strong> An event is looking for a speaker<br />
<strong>Action:</strong> Write and present a talk<br />
<strong>People:</strong> Marketing, Sales<br />
<strong>Examples</strong>: Marketing run the event with good attendance to drive MQLs. Sales use the content to show off platform capabilities with potential customers.</p>
<h2 id="conclusion" tabindex="-1">Conclusion</h2>
<p>Using internal triggers as your source of content ideas allows you to map your contributions to the goals of other teams. Many of these teams contribute directly to revenue, and if you make them successful it’s easy to show the impact you’re having on the business.</p>
<p>It might be tempting at this point to try to add some attribution directly to your team. You might negotiate that 2% of every sale where your material is used is attributed to your team.</p>
<p>This is a trap.</p>
<p>If you can claim 2% of every deal (and this is a high percentage), you need to do 100 deals worth $500,000 every single year to get $1 million in attribution. If you’re impacting 100 deals per year, chances are that you’ve got a large-ish team who are definitely costing more than $1 million per year.</p>
<p>Instead, focus on the relationships. <em>People trust people</em>. A VP saying “that DevRel team sure are awesome. They’ve cut our sales cycle by 2 weeks” is worth much more than 2% of a sale.</p>
Filter Google Analytics report2022-11-29T13:58:08Zhttps://michaelheap.com/filter-google-analytics-report/<p>I'm trying out <a href="https://getradar.co/">Radar</a> to keep track of analytics at a glance. It's Google Analytics collection works great, but the way that we've set up Google Analytics the data for multiple sites ends up in the same property.</p>
<p>I wanted to filter the report to only traffic to a specific subdomain. It took me too long to work out that I needed a <code>dimensionFilterClauses</code> entry with the <code>ga:pagePath</code> (via <a href="https://developers.google.com/analytics/devguides/reporting/data/v1/api-schema">the schema</a>).</p>
<p>Here's how to filter your reports in case you (or I) need to do it in the future:</p>
<pre class="shiki nord" style="background-color: #2e3440ff; color: #d8dee9ff"><div class="language-id">json</div><div class="code-container"><code><div class="line"><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #8FBCBB">reportRequests</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #8FBCBB">viewId</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">YOUR_VIEW_ID</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #8FBCBB">dateRanges</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #8FBCBB">startDate</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">today</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #8FBCBB">endDate</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">today</span><span style="color: #ECEFF4">"</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">],</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #8FBCBB">dimensions</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #8FBCBB">name</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">ga:date</span><span style="color: #ECEFF4">"</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">],</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #8FBCBB">metrics</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #8FBCBB">expression</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">ga:pageviews</span><span style="color: #ECEFF4">"</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">],</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #8FBCBB">dimensionFilterClauses</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #8FBCBB">filters</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #8FBCBB">dimensionName</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">ga:pagePath</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #8FBCBB">operator</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">PARTIAL</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #8FBCBB">expressions</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">docs.konghq.com</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">]</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">]</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">]</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">]</span></div><div class="line"><span style="color: #ECEFF4">}</span></div></code></div></pre>
<p>Other things that may be useful:</p>
<p><strong>Endpoint</strong>: <a href="https://analyticsreporting.googleapis.com/v4/reports:batchGet">https://analyticsreporting.googleapis.com/v4/reports:batchGet</a><br />
<strong>Method</strong>: <code>POST</code><br />
<strong>Auth</strong>: <code>Bearer [token]</code></p>
<p>Then transform the results using the following JS snippet:</p>
<pre class="shiki nord" style="background-color: #2e3440ff; color: #d8dee9ff"><div class="language-id">javascript</div><div class="code-container"><code><div class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">pageCount</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Number</span><span style="color: #D8DEE9FF">(</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">reports</span><span style="color: #D8DEE9FF">[</span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">]</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">data</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">totals</span><span style="color: #D8DEE9FF">[</span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">]</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">values</span><span style="color: #D8DEE9FF">[</span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">]</span></div><div class="line"><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">toLocaleString</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></div></code></div></pre>
Upgrade from Winston 2 to 3 using colors2022-10-23T11:39:59Zhttps://michaelheap.com/winston-3-upgrade-colors/<p>I updated a project from Winston 2 to Winston 3 this morning and needed to make a few changes to how it was configured.</p>
<p>I previously created a logger instance, then switched to using <code>syslog</code> levels with a custom colour scheme. Here's how I did it in Winston 2:</p>
<pre class="shiki nord" style="background-color: #2e3440ff; color: #d8dee9ff"><div class="language-id">javascript</div><div class="code-container"><code><div class="line"><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">winston</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">require</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">winston</span><span style="color: #ECEFF4">"</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></div><div class="line"></div><div class="line"><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">logger</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">new</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">winston</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Logger</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">transports</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> [</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">new</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">winston</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">transports</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Console</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">level</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">warning</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">colorize</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> ]</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></div><div class="line"></div><div class="line"><span style="color: #D8DEE9">syslogColors</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">debug</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">rainbow</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">info</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">cyan</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">notice</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">white</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">warning</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">yellow</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">error</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">bold red</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">crit</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">inverse yellow</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">alert</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">bold inverse red</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">emerg</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">bold inverse magenta</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></div><div class="line"><span style="color: #D8DEE9">winston</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">addColors</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">syslogColors</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></div><div class="line"><span style="color: #D8DEE9">logger</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">setLevels</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">winston</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">config</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">syslog</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">levels</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></div></code></div></pre>
<p>In Winston 3, the <code>new winston.Logger</code> call has been replaced with <code>winston.createLogger</code>, <code>levels</code> must be provided in the constructor, and formatting is handled by individual formatters using the <a href="https://github.com/winstonjs/logform">logform</a> library.</p>
<p>Here's how to achieve the same thing as above in Winston 3:</p>
<pre class="shiki nord" style="background-color: #2e3440ff; color: #d8dee9ff"><div class="language-id">javascript</div><div class="code-container"><code><div class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">winston</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">require</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">winston</span><span style="color: #ECEFF4">"</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></div><div class="line"></div><div class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">syslogColors</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">debug</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">rainbow</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">info</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">cyan</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">notice</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">white</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">warning</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">yellow</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">error</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">bold red</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">crit</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">inverse yellow</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">alert</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">bold inverse red</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">emerg</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">bold inverse magenta</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></div><div class="line"></div><div class="line"><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">format</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">winston</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">format</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">combine</span><span style="color: #D8DEE9FF">(</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">winston</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">format</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">colorize</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">all</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">colors</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">syslogColors</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">winston</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">format</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">simple</span><span style="color: #D8DEE9FF">()</span></div><div class="line"><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></div><div class="line"></div><div class="line"><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">logger</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">winston</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">createLogger</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">transports</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> [</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">new</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">winston</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">transports</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">Console</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">level</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">warning</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">format</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> ]</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">levels</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">winston</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">config</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">syslog</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">levels</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></div></code></div></pre>
Running `gickup` on a QNAP NAS2022-10-15T18:00:01Zhttps://michaelheap.com/gickup-on-qnap/<p>I learned about <a href="https://github.com/cooperspencer/gickup">Gickup</a> whilst reading <a href="https://github.com/geerlingguy/my-backup-plan">Jeff Geerling’s backup plan</a> and thought it would be a useful thing to run for my own repos.</p>
<p>I initially ran it on my local machine periodically, then realised that I could run it on my QNAP NAS every night. Here’s how I did it:</p>
<ul>
<li>Create a shared folder named <code>gickup</code> to contain the backups. I’ve got a volume named <code>Backups</code> that contains this, but any volume will do</li>
<li>Create a directory to store your backups by running <code>mkdir -p /share/gickup/repos</code></li>
<li>Edit <code>/etc/config/crontab</code> and add the following line to the end: <code>0 3 * * * docker run --rm -v /share/gickup:/hostvol buddyspencer/gickup:0.9 /gickup/app /hostvol/config.yml > /share/gickup/last-run.log 2>&1</code>. This will run <code>gickup</code> every day at 3am</li>
<li>Reload cron to pick up that change by running <code>crontab /etc/config/crontab && /etc/init.d/crond.sh restart</code></li>
</ul>
<p>You may have noticed that I reference <code>/hostvol/config.yml</code>. I mount the <code>/share/gickup</code> folder a <code>/hostvol</code>, and <code>config.yml</code> is my <code>gickup</code> configuration file. Here’s what it looks like:</p>
<pre class="shiki nord" style="background-color: #2e3440ff; color: #d8dee9ff"><div class="language-id">yaml</div><div class="code-container"><code><div class="line"><span style="color: #616E88"># config.yml in /share/gickup</span></div><div class="line"><span style="color: #8FBCBB">source</span><span style="color: #ECEFF4">:</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">github</span><span style="color: #ECEFF4">:</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">token</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">CHANGE_ME_TO_YOUR_GITHUB_PAT</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">excludeorgs</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88"># this excludes repos from the organizations "foo" and "bar"</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">foo</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">bar</span></div><div class="line"><span style="color: #8FBCBB">destination</span><span style="color: #ECEFF4">:</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">local</span><span style="color: #ECEFF4">:</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">path</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">/hostvol/repos</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">structured</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></div></code></div></pre>
<p>Create your own <code>config.yml</code>, making sure you change the <code>token</code> value. If you’ve configured all of the above, all the repositories you have access to will be backed up at 3am each day.</p>
Transcribe audio locally on MacOS2022-10-14T12:01:20Zhttps://michaelheap.com/transcribe-folder-locally-macos/<p>I've been using <a href="https://apps.apple.com/us/app/just-press-record/id1033342465">Just Press Record</a> on my watch to capture things that are on my mind whilst working outdoors. I'm usually pretty good at processing the recordings regularly, but recently found a folder of recordings that I may or may not have processed.</p>
<p>Rather than listening to all of the recordings, I wanted to batch transcribe them and read the text. There are plenty of online tools available, but I wanted a local CLI tool which lead me to <a href="https://github.com/sveinbjornt/hear">hear</a>.</p>
<p>The 0.1 release of <a href="https://github.com/sveinbjornt/hear">hear</a> is from March and is missing some important bugfixes. I cloned the repo and ran <code>make</code> to build the tool locally and it works great.</p>
<p><code>hear</code> accepts an input file and a language, and writes the output to <code>stdout</code>. Here's an example that processes a file against the <code>en-GB</code> model and writes out <code>transcription.txt</code>:</p>
<pre class="shiki nord" style="background-color: #2e3440ff; color: #d8dee9ff"><div class="language-id">bash</div><div class="code-container"><code><div class="line"><span style="color: #D8DEE9FF">hear -i ./2022-01-29/10-17-46.m4a -l en-GB </span><span style="color: #81A1C1">></span><span style="color: #D8DEE9FF"> transcription.txt</span></div></code></div></pre>
<blockquote>
<p>Important: You may see the word <code>abort</code> when running <code>hear</code>. This is due to <code>hear</code> needing to request microphone permissions. If you get an error, try running <code>hear</code> in <code>Terminal.app</code> rather than any other application</p>
</blockquote>
<p>If you have a whole folder of recordings to transcribe, you might find this snippet useful. It produces a <code>.txt</code> file next to each recording with the contents of the transcription:</p>
<pre class="shiki nord" style="background-color: #2e3440ff; color: #d8dee9ff"><div class="language-id">bash</div><div class="code-container"><code><div class="line"><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">`</span><span style="color: #A3BE8C">find </span><span style="color: #88C0D0">.</span><span style="color: #A3BE8C"> -name </span><span style="color: #ECEFF4">'</span><span style="color: #A3BE8C">*.m4a</span><span style="color: #ECEFF4">'`</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">do</span><span style="color: #D8DEE9FF"> hear -i </span><span style="color: #81A1C1">$</span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> -l en-GB </span><span style="color: #81A1C1">></span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">${</span><span style="color: #D8DEE9">i</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9">.m4a</span><span style="color: #81A1C1">/}</span><span style="color: #D8DEE9FF">.txt</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">done</span></div></code></div></pre>
<p>Here's what it looks like for me after running that command (with some folders removed for brevity):</p>
<pre class="shiki nord" style="background-color: #2e3440ff; color: #d8dee9ff"><div class="code-container"><code><div class="line"><span style="color: undefined">❯ ls -R
2021-12-27 2022-01-29
./2021-12-27:
14-17-39.m4a 14-19-34.m4a 14-21-18.m4a 14-21-54.m4a 14-49-09.m4a 14-53-17.m4a
14-17-39.txt 14-19-34.txt 14-21-18.txt 14-21-54.txt 14-49-09.txt 14-53-17.txt
./2022-01-29:
10-17-46.m4a 10-17-46.txt 10-56-59.m4a 10-56-59.txt</span></div></code></div></pre>
Secrets Management in Kong is Now GA!2022-10-10T00:00:01Zhttps://michaelheap.com/kong-secrets-management/<p>See <a href="https://konghq.com/blog/secrets-management">https://konghq.com/blog/secrets-management</a></p>
Mock `process.env` using `mocked-env`2022-10-07T10:18:07Zhttps://michaelheap.com/mock-environment/<p>When testing values in <code>process.env</code>, they persist for all further tests in your test suite. This can have unwanted side effects, making tests pass or fail unintentionally.</p>
<p>Using <code>mocked-env</code>, you can reset the environment to the pre-test state after every test runs. It works by taking a copy of <code>process.env</code> when you call <code>mockEnv</code>, then restores it once the test has run (<code>afterEach</code>).</p>
<p>Here's an example that shows how environment variables set earlier in the test suite don't persist in to later tests:</p>
<pre class="shiki nord" style="background-color: #2e3440ff; color: #d8dee9ff"><div class="language-id">javascript</div><div class="code-container"><code><div class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">mockEnv</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">require</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">mocked-env</span><span style="color: #ECEFF4">"</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></div><div class="line"></div><div class="line"><span style="color: #616E88">// Variables to store references to what we need to reset</span></div><div class="line"><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">restore</span><span style="color: #81A1C1">;</span></div><div class="line"><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">restoreTest</span><span style="color: #81A1C1">;</span></div><div class="line"></div><div class="line"><span style="color: #88C0D0">beforeEach</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=></span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">restore</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">mockEnv</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">VALUE_ONE</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">Hello</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">restoreTest</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=></span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{}</span><span style="color: #81A1C1">;</span></div><div class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></div><div class="line"></div><div class="line"><span style="color: #88C0D0">afterEach</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=></span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">restore</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">restoreTest</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></div><div class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></div><div class="line"></div><div class="line"><span style="color: #88C0D0">test</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">environment concatenation (world)</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=></span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">restoreTest</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">mockEnv</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">VALUE_TWO</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">World</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></div><div class="line"></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">expect</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">concatenateValuesFromEnv</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">VALUE_ONE</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">VALUE_TWO</span><span style="color: #ECEFF4">"</span><span style="color: #D8DEE9FF">))</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">toBe</span><span style="color: #D8DEE9FF">(</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">Hello World</span><span style="color: #ECEFF4">"</span></div><div class="line"><span style="color: #D8DEE9FF"> )</span><span style="color: #81A1C1">;</span></div><div class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></div><div class="line"></div><div class="line"><span style="color: #88C0D0">test</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">environment concatenation (michael)</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=></span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">restoreTest</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">mockEnv</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">VALUE_TWO</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">Michael</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></div><div class="line"></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">expect</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">concatenateValuesFromEnv</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">VALUE_ONE</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">VALUE_TWO</span><span style="color: #ECEFF4">"</span><span style="color: #D8DEE9FF">))</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">toBe</span><span style="color: #D8DEE9FF">(</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">Hello Michael</span><span style="color: #ECEFF4">"</span></div><div class="line"><span style="color: #D8DEE9FF"> )</span><span style="color: #81A1C1">;</span></div><div class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></div><div class="line"></div><div class="line"><span style="color: #88C0D0">test</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">environment concatenation (missing value)</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=></span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">expect</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">concatenateValuesFromEnv</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">VALUE_ONE</span><span style="color: #ECEFF4">"</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">VALUE_TWO</span><span style="color: #ECEFF4">"</span><span style="color: #D8DEE9FF">))</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">toBe</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">"</span><span style="color: #A3BE8C">Hello </span><span style="color: #ECEFF4">"</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></div><div class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></div><div class="line"></div><div class="line"><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">concantenateValuesFromEnv</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">a</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">b</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">`</span><span style="color: #81A1C1">${</span><span style="color: #D8DEE9">process</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9">a</span><span style="color: #ECEFF4">]</span><span style="color: #81A1C1">}</span><span style="color: #A3BE8C"> </span><span style="color: #81A1C1">${</span><span style="color: #D8DEE9">process</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9">b</span><span style="color: #ECEFF4">]</span><span style="color: #81A1C1">}</span><span style="color: #ECEFF4">`</span><span style="color: #81A1C1">;</span></div><div class="line"><span style="color: #ECEFF4">}</span></div></code></div></pre>
<p>Without using <code>mocked-env</code>, the last test would fail as the returned value would be <code>Hello Michael</code> as <code>VALUE_TWO</code> is set in the previous test, and would not be cleaned up without <code>mocked-env</code>.</p>
Using `GITHUB_TOKEN` as a default `input` value2022-10-07T10:10:27Zhttps://michaelheap.com/default-github-action-token/<p>If you need to authenticate against the GitHub API in your GitHub Action, you might prompt the user to provide their <code>GITHUB_TOKEN</code> secret through an input. This isn't necessarily needed thanks to the <code>github.token</code> context and default input values.</p>
<p>You can gain access to the <code>GITHUB_TOKEN</code> secret by default by configuring your <code>action.yml</code> file to access <code>github.token</code>:</p>
<pre class="shiki nord" style="background-color: #2e3440ff; color: #d8dee9ff"><div class="language-id">yaml</div><div class="code-container"><code><div class="line"><span style="color: #8FBCBB">name</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">Action Name</span></div><div class="line"><span style="color: #8FBCBB">description</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">My awesome description goes here</span></div><div class="line"><span style="color: #8FBCBB">runs</span><span style="color: #ECEFF4">:</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">using</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">node12</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">main</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">dist/index.js</span></div><div class="line"><span style="color: #8FBCBB">inputs</span><span style="color: #ECEFF4">:</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">token</span><span style="color: #ECEFF4">:</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">description</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">The GitHub token to use when calling the API</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">default</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">${{ github.token }}</span></div><div class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">required</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></div></code></div></pre>
Announcing General Availability of Kong Gateway 3.02022-09-28T00:00:01Zhttps://michaelheap.com/kong-gateway-3-0/<p>See <a href="https://konghq.com/blog/kong-gateway-3-0">https://konghq.com/blog/kong-gateway-3-0</a></p>
API Composition with StepZen and Kong2022-09-15T00:00:01Zhttps://michaelheap.com/kong-api-composition-with-stepzen/<p>See <a href="https://konghq.com/blog/api-composition-with-stepzen-and-kong">https://konghq.com/blog/api-composition-with-stepzen-and-kong</a></p>