Congratulations!

[Valid RSS] This is a valid RSS feed.

Recommendations

This feed is valid, but interoperability with the widest range of feed readers could be improved by implementing the following recommendations.

Source: http://akrabat.com/feed/

  1. <?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
  2. xmlns:content="http://purl.org/rss/1.0/modules/content/"
  3. xmlns:wfw="http://wellformedweb.org/CommentAPI/"
  4. xmlns:dc="http://purl.org/dc/elements/1.1/"
  5. xmlns:atom="http://www.w3.org/2005/Atom"
  6. xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
  7. xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
  8. >
  9.  
  10. <channel>
  11. <title>Rob Allen</title>
  12. <atom:link href="https://akrabat.com/feed/" rel="self" type="application/rss+xml" />
  13. <link>https://akrabat.com</link>
  14. <description>Pragmatism in the real world</description>
  15. <lastBuildDate>Tue, 25 Feb 2025 11:53:14 +0000</lastBuildDate>
  16. <language>en-US</language>
  17. <sy:updatePeriod>
  18. hourly </sy:updatePeriod>
  19. <sy:updateFrequency>
  20. 1 </sy:updateFrequency>
  21. <generator>https://wordpress.org/?v=6.7.2</generator>
  22. <item>
  23. <title>Privileges app: sudo for macOS</title>
  24. <link>https://akrabat.com/privileges-app-sudo-for-macos/</link>
  25. <comments>https://akrabat.com/privileges-app-sudo-for-macos/#respond</comments>
  26. <dc:creator><![CDATA[Rob]]></dc:creator>
  27. <pubDate>Tue, 11 Mar 2025 11:00:00 +0000</pubDate>
  28. <category><![CDATA[Computing]]></category>
  29. <category><![CDATA[Mac]]></category>
  30. <guid isPermaLink="false">https://akrabat.com/?p=7279</guid>
  31.  
  32. <description><![CDATA[By default, the first user that you create on macOS is an administrator and has more power over the system than a normal user account. The reason it does this is obvious as you need this power to create other users, to install software and so on. However it also means that the computer is slightly more vulnerable to attack via a vulnerability in the software I use day to day. To combat this, I… <a href="https://akrabat.com/privileges-app-sudo-for-macos/">continue reading</a>.]]></description>
  33. <content:encoded><![CDATA[<p>By default, the first user that you create on macOS is an administrator and has more power over the system than a normal user account. The reason it does this is obvious as you need this power to create other users, to install software and so on. However it also means that the computer is slightly more vulnerable to attack via a vulnerability in the software I use day to day.</p>
  34. <p>To combat this, I run with my user account as a normal user and have a separate admin account for privilege escalation. This is remarkably easy on macOS as whenever the system needs to do something that needs more rights than my user, it pops up a dialog asking for the credentials of an administrative user.</p>
  35. <p>When I have a lot of work to do that requires an administrative user, typing in the admin user's creds for every action is tedious. What I need is something like <tt>sudo</tt>, but for my Mac, so I use the <a href="https://github.com/SAP/macOS-enterprise-privileges?tab=readme-ov-file#privileges">Privileges</a> app from SAP for this. This app provides a way to request admin rights for my current user account so that I can then do all the admin tasks without having to keep entering credentials.</p>
  36. <p><img fetchpriority="high" decoding="async" src="https://akrabat.com/wp-content/uploads/2025/02/2025privileges1.png" alt="" title="privileges1.png" border="0" width="300" height="353" /></p>
  37. <p>Once I request privileges, my account has admin rights until they either expire, I remove them or a I reboot. The time limit is configured in the Settings and I have it set to 30 minutes as it's rare that I sill need admin rights after that long and I never remember to turn them off. </p>
  38. <p>It's nice and easy, and my Mac is that much more secure. I highly recommend that you try it.</p>
  39. ]]></content:encoded>
  40. <wfw:commentRss>https://akrabat.com/privileges-app-sudo-for-macos/feed/</wfw:commentRss>
  41. <slash:comments>0</slash:comments>
  42. </item>
  43. <item>
  44. <title>Cloudflare SSL/TLS setting for an encrypted connection to upstream</title>
  45. <link>https://akrabat.com/cloudflare-ssl-tls-setting-for-an-encrypted-connection-to-upstream/</link>
  46. <comments>https://akrabat.com/cloudflare-ssl-tls-setting-for-an-encrypted-connection-to-upstream/#respond</comments>
  47. <dc:creator><![CDATA[Rob]]></dc:creator>
  48. <pubDate>Tue, 04 Mar 2025 11:00:00 +0000</pubDate>
  49. <category><![CDATA[Computing]]></category>
  50. <guid isPermaLink="false">https://akrabat.com/?p=7266</guid>
  51.  
  52. <description><![CDATA[I was helping a friend set up a new website and hit a problem where the website was in an infinite redirect loop: I could see this in curl quite easily: $ curl -I https://myfriendswebsite.example.com/ HTTP/1.1 301 Moved Permanently Location: https://myfriendswebsite.example.com/ To debug, we turned off Cloudflare by setting the Proxy status on the DNS record in Cloudflare's admin to "DNS only" and the problem went away. Further investigation led to the SSL/TLS setting in… <a href="https://akrabat.com/cloudflare-ssl-tls-setting-for-an-encrypted-connection-to-upstream/">continue reading</a>.]]></description>
  53. <content:encoded><![CDATA[<p>I was helping a friend set up a new website and hit a problem where the website was in an infinite redirect loop:</p>
  54. <p>I could see this in <tt>curl</tt> quite easily:</p>
  55. <pre>
  56. $ curl -I https://myfriendswebsite.example.com/
  57. HTTP/1.1 301 Moved Permanently
  58. Location: https://myfriendswebsite.example.com/
  59. </pre>
  60. <p>To debug, we turned off Cloudflare by setting the Proxy status on the DNS record in Cloudflare's admin to "DNS only" and the problem went away.</p>
  61. <p>Further investigation led to the SSL/TLS setting in Cloudflare. When my friend had set this up, they had chosen the <a href="https://developers.cloudflare.com/ssl/origin-configuration/ssl-modes/flexible/"><em>Flexible</em></a> encryption mode, possibly as this sounded like the right choice. (Though I wonder why they didn't leave it on automatic?)</p>
  62. <p>With <em>Flexible</em> encryption mode, Cloudflare accepts an SSL connection and then uses an non-SSL connection to our server where the website is hosted (<tt>http://myfriendswebsite.example.com/</tt>). Our server has an SSL certificate set-up and an nginx rule that noted that the request was not encrypted and so returned a 301 redirect to the secure url (<tt>https://myfriendswebsite.example.com/</tt>). The browser redirected back to the SSL URL which went to Cloudflare, which made a non-SSL connection to the web server and we enter the loop.</p>
  63. <p>The fix is simple: Set the Cloudflare encryption mode to <a href="https://developers.cloudflare.com/ssl/origin-configuration/ssl-modes/full-strict/"><em>Full (Strict)</em></a> if you have your own SSL certificate on the upstream server. </p>
  64. <p>Alternatively, don't mess with this setting, leave it on <em>Automatic</em> and let Cloudflare sort it out, though this runs the risk that you'll have a non-SSL connection between Cloudflare and your web server if you've misconfigured your web server. Personally, I prefer <em>Full (Strict)</em>, so that there's a failure if my web server is no longer serving encrypted traffic to Cloudflare with a valid certificate.</p>
  65. ]]></content:encoded>
  66. <wfw:commentRss>https://akrabat.com/cloudflare-ssl-tls-setting-for-an-encrypted-connection-to-upstream/feed/</wfw:commentRss>
  67. <slash:comments>0</slash:comments>
  68. </item>
  69. <item>
  70. <title>Converting a PHPUnit TestListener to an Event Subscriber</title>
  71. <link>https://akrabat.com/converting-a-phpunit-testlistener-to-an-event-subscriber/</link>
  72. <comments>https://akrabat.com/converting-a-phpunit-testlistener-to-an-event-subscriber/#respond</comments>
  73. <dc:creator><![CDATA[Rob]]></dc:creator>
  74. <pubDate>Tue, 25 Feb 2025 11:00:00 +0000</pubDate>
  75. <category><![CDATA[PHP]]></category>
  76. <guid isPermaLink="false">https://akrabat.com/?p=7276</guid>
  77.  
  78. <description><![CDATA[One of the bigger changes in PHPUnit 10 was the introduction of the new extension system which replaced listeners and hooks. The old way On one of my projects we have a TestListener that sets up the database before we run some functional tests against it. It looks like this: &#60;?php declare(strict_types=1); namespace App\Test\Listeners; use App\Test\Functional\Helpers\DbHelper; use PHPUnit\Framework\TestListenerDefaultImplementation; use PHPUnit\Framework\TestSuite; class TestListener implements \PHPUnit\Framework\TestListener { use TestListenerDefaultImplementation; public function startTestSuite(TestSuite $suite): void { if (str_contains($suite-&#62;getName(),… <a href="https://akrabat.com/converting-a-phpunit-testlistener-to-an-event-subscriber/">continue reading</a>.]]></description>
  79. <content:encoded><![CDATA[<p>One of the bigger changes in PHPUnit 10 was the introduction of the <a href="https://docs.phpunit.de/en/10.5/extending-phpunit.html#extending-the-test-runner">new extension system</a> which replaced listeners and hooks. </p>
  80. <h2>The old way</h2>
  81. <p>On one of my projects we have a <tt>TestListener</tt> that sets up the database before we run some functional tests against it.</p>
  82. <p>It looks like this:</p>
  83. <pre>
  84. &lt;?php
  85.  
  86. declare(strict_types=1);
  87.  
  88. namespace App\Test\Listeners;
  89.  
  90. use App\Test\Functional\Helpers\DbHelper;
  91. use PHPUnit\Framework\TestListenerDefaultImplementation;
  92. use PHPUnit\Framework\TestSuite;
  93.  
  94. class TestListener implements \PHPUnit\Framework\TestListener
  95. {
  96.    use TestListenerDefaultImplementation;
  97.  
  98.    public function startTestSuite(TestSuite $suite): void
  99.    {
  100.        if (str_contains($suite-&gt;getName(), 'functional')) {
  101.            DbHelper::setup();
  102.        }
  103.    }
  104. }
  105. </pre>
  106. <p>As the PHPUnit <tt>TestListener</tt> interface defines a lot of methods, you can use the <tt>TestListenerDefaultImplementation</tt> trait to stub them all out and only write the ones you care about. In our case, we care about <tt>startTestSuite()</tt> as that's run when a test suite is started (the clue is in the name!). Our implementation is trivial: if the name of the suite is <tt>functional</tt>, then call <tt>DbHelper::setup()</tt>.</p>
  107. <p>It's registered in <tt>phpunit.xml</tt> like this:</p>
  108. <pre>
  109.    &lt;listeners&gt;
  110.        &lt;listener class=&quot;App\Test\Listeners\TestListener&quot;&gt;&lt;/listener&gt;
  111.    &lt;/listeners&gt;
  112. </pre>
  113. <h2>The new way</h2>
  114. <p><tt>TestListener</tt> was removed in PHPUnit 10 and so we had to replace this with a new extension. You need an <tt>Extension</tt> class to that is registered with PHPUnit and in turn registers as many <tt>Subscriber</tt> classes you need as a subscriber can only lister to one event.</p>
  115. <h3>The Extension</h3>
  116. <p>I called our extension <tt>SetupDatabaseBeforeFunctionalTestsExtension</tt>. It looks like this:</p>
  117. <pre>
  118. &lt;?php
  119.  
  120. declare(strict_types=1);
  121.  
  122. namespace App\Test\Extension;
  123.  
  124. use App\Test\Helper\DbHelper;
  125. use PHPUnit\Runner\Extension\Extension;
  126. use PHPUnit\Runner\Extension\Facade;
  127. use PHPUnit\Runner\Extension\ParameterCollection;
  128. use PHPUnit\TextUI\Configuration\Configuration;
  129.  
  130. final class SetupDatabaseBeforeFunctionalTestsExtension implements Extension
  131. {
  132.    public function bootstrap(Configuration $configuration, Facade $facade, ParameterCollection $parameters): void
  133.    {
  134.        $facade-&gt;registerSubscribers(
  135.            new SetupDatabaseBeforeFunctionalTests()
  136.        );
  137.    }
  138. }
  139. </pre>
  140. <p>This class implements the <tt>Extension</tt> interface which means that we need to implement the <tt>bootstrap()</tt> method. You can set up things in here and then register your subscribers. In our case, we only need to register the <tt>SetupDatabaseBeforeFunctionalTests</tt> subscriber.</p>
  141. <p>To register an extension with PHPUnit, you add it to <tt>phpunit.xml</tt>, like this:</p>
  142. <pre>
  143.    &lt;extensions&gt;
  144.        &lt;bootstrap class=&quot;App\Test\Extension\SetupDatabaseBeforeFunctionalTestsExtension&quot;/&gt;
  145.    &lt;/extensions&gt;
  146. </pre>
  147. <p>The implementation of <tt>SetupDatabaseBeforeFunctionalTests</tt> is relatively simple:</p>
  148. <pre>
  149. &lt;?php
  150.  
  151. declare(strict_types=1);
  152.  
  153. namespace App\Test\Extension;
  154.  
  155. use App\Test\Helper\DbHelper;
  156. use PHPUnit\Event;
  157. use PHPUnit\Event\TestSuite\Started;
  158.  
  159. final class SetupDatabaseBeforeFunctionalTests implements Event\TestSuite\StartedSubscriber
  160. {
  161.    public function notify(Started $event): void
  162.    {
  163.        if (str_contains($event-&gt;testSuite()-&gt;name(), 'functional')) {
  164.            DbHelper::setup();
  165.        }
  166.    }
  167. }
  168. </pre>
  169. <p>A subscriber implements the relevant interface for the event that you want to subscribe to. There are many events, which are <a href="https://docs.phpunit.de/en/10.5/events.html">listed in the docs</a> and you can infer the interface name by appending the word "Subscriber" to the event name.</p>
  170. <p>In our case, we want to subscribe to the <tt>PHPUnit\Event\TestSuite\Started</tt> event, so we implement the <tt>PhpUnit\Event\TestSuite\StartedSubscriber</tt> interface. Our subscriber then implements the <tt>notify()</tt> method where the <tt>$event</tt> is passed in. Unsurprisingly, the code for our <tt>notify()</tt> is essentially the same as we used in our <tt>TestListener</tt>.</p>
  171. <p>That's it. We're done.</p>
  172. <h2>A simplification to one class</h2>
  173. <p>As is clear by the name of my extension, this extension will only ever do one thing; set up the database before running the functional test suite. For a case like this, we can consolidate to a single class by creating an anonymous subscriber class and put it directly into the extension class:</p>
  174. <pre>
  175. &lt;?php
  176.  
  177. declare(strict_types=1);
  178.  
  179. namespace App\Test\Extension;
  180.  
  181. use App\Test\Helper\DbHelper;
  182. use PHPUnit\Event\TestSuite\Started;
  183. use PHPUnit\Event\TestSuite\StartedSubscriber;
  184. use PHPUnit\Runner\Extension\Extension;
  185. use PHPUnit\Runner\Extension\Facade;
  186. use PHPUnit\Runner\Extension\ParameterCollection;
  187. use PHPUnit\TextUI\Configuration\Configuration;
  188.  
  189. final class SetupDatabaseBeforeFunctionalTestsExtension implements Extension
  190. {
  191.    public function bootstrap(Configuration $configuration, Facade $facade, ParameterCollection $parameters): void
  192.    {
  193.        $facade-&gt;registerSubscribers(
  194.            new class implements StartedSubscriber {
  195.                public function notify(Started $event): void
  196.                {
  197.                    if (str_contains($event-&gt;testSuite()-&gt;name(), 'functional')) {
  198.                        DbHelper::setup();
  199.                    }
  200.                }
  201.            }
  202.        );
  203.    }
  204. }
  205. </pre>
  206. <p>Nothing else changes and my database is set up before we run the test suite and I can now upgrade the rest of my tests to support PHPUnit 10.</p>
  207. ]]></content:encoded>
  208. <wfw:commentRss>https://akrabat.com/converting-a-phpunit-testlistener-to-an-event-subscriber/feed/</wfw:commentRss>
  209. <slash:comments>0</slash:comments>
  210. </item>
  211. <item>
  212. <title>OAuth scopes vs user roles</title>
  213. <link>https://akrabat.com/oauth-scopes-vs-user-roles/</link>
  214. <comments>https://akrabat.com/oauth-scopes-vs-user-roles/#respond</comments>
  215. <dc:creator><![CDATA[Rob]]></dc:creator>
  216. <pubDate>Tue, 18 Feb 2025 11:00:00 +0000</pubDate>
  217. <category><![CDATA[Development]]></category>
  218. <guid isPermaLink="false">https://akrabat.com/?p=7264</guid>
  219.  
  220. <description><![CDATA[There is a different between OAuth scopes and roles. A scope is the abilities that the client requests that the user can then decide if they are going to authorise that client to do those things. A role is the rights that a given user has within the application. Scope examples are "read-name", "read-address", "read-email", 'write-all", etc. Example roles might be "moderator", "user", "administrator", etc. and is used by the app to determine what the… <a href="https://akrabat.com/oauth-scopes-vs-user-roles/">continue reading</a>.]]></description>
  221. <content:encoded><![CDATA[<p>There is a different between OAuth scopes and roles.</p>
  222. <p>A <em>scope</em> is the abilities that the client requests that the user can then decide if they are going to authorise that client to do those things.</p>
  223. <p>A <em>role</em> is the rights that a given user has within the application.</p>
  224. <p>Scope examples are "read-name", "read-address", "read-email", 'write-all", etc. Example roles might be "moderator", "user", "administrator", etc. and is used by the app to determine what the user can do regardless of which client the user is using to perform the action.</p>
  225. <p>If a user authorises a client with only the "read-name" <em>scope</em>, then that client tries to update the name, it will fail, regardless of whether the user's <em>role</em> allows them to perform that functionality.</p>
  226. ]]></content:encoded>
  227. <wfw:commentRss>https://akrabat.com/oauth-scopes-vs-user-roles/feed/</wfw:commentRss>
  228. <slash:comments>0</slash:comments>
  229. </item>
  230. <item>
  231. <title>Writing out the page contents in Playwright</title>
  232. <link>https://akrabat.com/writing-out-the-page-contents-in-playwright/</link>
  233. <comments>https://akrabat.com/writing-out-the-page-contents-in-playwright/#respond</comments>
  234. <dc:creator><![CDATA[Rob]]></dc:creator>
  235. <pubDate>Tue, 11 Feb 2025 11:00:00 +0000</pubDate>
  236. <category><![CDATA[Development]]></category>
  237. <category><![CDATA[JS]]></category>
  238. <guid isPermaLink="false">https://akrabat.com/?p=7262</guid>
  239.  
  240. <description><![CDATA[I recently had a problem with a failing Playwright test that only happened when running in Docker. The test that was failing was: let locator = page.locator('a[href="/login"].nav-link'); await locator.click(); await expect(page).toHaveTitle(/Log in/); The test clicks the link to go to /login and then checks that the next page's title contains the text "Log in". Not an especially complicated test, so I was quite surprised when it failed claiming that the title was blank. To work… <a href="https://akrabat.com/writing-out-the-page-contents-in-playwright/">continue reading</a>.]]></description>
  241. <content:encoded><![CDATA[<p>I recently had a problem with a failing <a href="https://playwright.dev">Playwright</a> test that only happened when running in Docker.</p>
  242. <p>The test that was failing was:</p>
  243. <pre>
  244. let locator = page.locator('a[href="/login"].nav-link');
  245. await locator.click();
  246.  
  247. await expect(page).toHaveTitle(/Log in/);
  248. </pre>
  249. <p>The test clicks the link to go to /login and then checks that the next page's title contains the text "Log in". Not an especially complicated test, so I was quite surprised when it failed claiming that the title was blank.</p>
  250. <p>To work out what was happening, I wanted to view the HTML that the Playwright browser was seeing. To do this I added this after the <tt>click()</tt> call.:</p>
  251. <pre>
  252. await page.waitForLoadState();
  253. console.log("Page content: ", await page.content());
  254. </pre>
  255. <p>The HTML for the page was then rendered into my terminal and showed that the HTML wasn't what I expected. It was then <em>relatively easy</em> to sort the problem and all was well again.</p>
  256. ]]></content:encoded>
  257. <wfw:commentRss>https://akrabat.com/writing-out-the-page-contents-in-playwright/feed/</wfw:commentRss>
  258. <slash:comments>0</slash:comments>
  259. </item>
  260. <item>
  261. <title>Activating rather than relaunching a menu bar app on macOS</title>
  262. <link>https://akrabat.com/activating-rather-than-relaunching-a-menu-bar-app-on-macos/</link>
  263. <comments>https://akrabat.com/activating-rather-than-relaunching-a-menu-bar-app-on-macos/#respond</comments>
  264. <dc:creator><![CDATA[Rob]]></dc:creator>
  265. <pubDate>Tue, 04 Feb 2025 11:00:00 +0000</pubDate>
  266. <category><![CDATA[AppleScript]]></category>
  267. <category><![CDATA[Computing]]></category>
  268. <guid isPermaLink="false">https://akrabat.com/?p=7259</guid>
  269.  
  270. <description><![CDATA[One very minor thing that's been bugging me since macOS Sequoia came out is that if you launch an app that lives in your menu bar, but also has a hidden Dock icon a second time, then the Dock icon will re-appear. This happens to me a lot because I use Alfred to launch apps and also to bring an app to the front. This works because opening a running app will bring it to… <a href="https://akrabat.com/activating-rather-than-relaunching-a-menu-bar-app-on-macos/">continue reading</a>.]]></description>
  271. <content:encoded><![CDATA[<p>One very minor thing that's been bugging me since macOS Sequoia came out is that if you launch an app that lives in your menu bar, but also has a hidden Dock icon a second time, then the Dock icon will re-appear.</p>
  272. <p>This happens to me a lot because I use <a href="https://www.alfredapp.com">Alfred</a> to launch apps and also to bring an app to the front. This works because opening a running app will bring it to the foreground. You can test this using the Terminal with the <tt>open -a</tt> command if you want to.</p>
  273. <p>I've used this workflow for years with the <a href="https://www.getharvest.com/apps/mac">Harvest</a> app. Harvest lives in my menu bar and can be accessed using the mouse. However, I tend to just open it again with Alfred which opens the main window which is anchored to the menu bar icon and then press space to stop the current timer or <tt>cmd+n</tt> to enter the details to start a new one.</p>
  274. <p>Since Sequoia, I've noticed that when I use Alfred to open Harvest's main window, the Harvest icon reappears on my Dock. I don't want this, so I wrote an Alfed Workflow to fix it.</p>
  275. <h2>Alfred Workflow</h2>
  276. <p>The workflow consists of two blocks: a Keyword input and an AppleScript action.</p>
  277. <p><img decoding="async" src="https://akrabat.com/wp-content/uploads/2025/01/alfred-harvest-workflow.png" alt="Alfred harvest workflow." border="0" width="340" height="133" /></p>
  278. <p>The Keyword input block simply responds to the word "Harvest" as that's what I type:</p>
  279. <p><img decoding="async" src="https://akrabat.com/wp-content/uploads/2025/01/alfred-harvest-keyword-input.png" alt="Alfred harvest keyword input." border="0" width="450" height="258" /></p>
  280. <p>Then, to either start Harvest, or activate it, I use some AppleScript:</p>
  281. <pre lang="AppleScript">
  282. tell application "System Events"
  283.    if not (exists process "Harvest") then
  284.        tell application "Harvest" to launch
  285.    else
  286.        tell application "Harvest" to activate
  287.    end if
  288. end tell
  289. </pre>
  290. <p>AppleScript remains one of the harder languages I write and I have to look things up every time, but at least LLMs make this easier. Once written, it's quite easier to see what it does. The magic is that with this script we activate it rather than launch it once running.</p>
  291. <p>In Alfred, it looks like this:</p>
  292. <p><img loading="lazy" decoding="async" src="https://akrabat.com/wp-content/uploads/2025/01/alfred-harvest-applescript.png" alt="Alfred harvest applescript." border="0" width="500" height="362" /></p>
  293. <h2>Training Alfred</h2>
  294. <p>That's it. All I needed to do then was continually use Alfred with variations of "H", "Ha", "Har", etc to train it to put my new "Open Harvest" action at the top of the list.</p>
  295. ]]></content:encoded>
  296. <wfw:commentRss>https://akrabat.com/activating-rather-than-relaunching-a-menu-bar-app-on-macos/feed/</wfw:commentRss>
  297. <slash:comments>0</slash:comments>
  298. </item>
  299. <item>
  300. <title>Using uv as your shebang line</title>
  301. <link>https://akrabat.com/using-uv-as-your-shebang-line/</link>
  302. <comments>https://akrabat.com/using-uv-as-your-shebang-line/#comments</comments>
  303. <dc:creator><![CDATA[Rob]]></dc:creator>
  304. <pubDate>Tue, 28 Jan 2025 11:00:00 +0000</pubDate>
  305. <category><![CDATA[Python]]></category>
  306. <guid isPermaLink="false">https://akrabat.com/?p=7254</guid>
  307.  
  308. <description><![CDATA[I create a fair few scripts in my ~/bin/ directory to automate tasks. Since discovering uv and inline script metadata, I've started using Python far more for these. As ~/bin is on my path, I want to run the script by calling it directly on the command line. To do this, I use this shebang: #!/usr/bin/env -S uv run --script The command line will now run uv run --script and pass the file as the… <a href="https://akrabat.com/using-uv-as-your-shebang-line/">continue reading</a>.]]></description>
  309. <content:encoded><![CDATA[<p>I create a fair few scripts in my <tt>~/bin/</tt> directory to automate tasks. Since discovering <a href="https://akrabat.com/defining-python-dependencies-at-the-top-of-the-file/"><tt>uv</tt> and inline script metadata</a>, I've started using Python far more for these.</p>
  310. <p>As <tt>~/bin</tt> is on my path, I want to run the script by calling it directly on the command line. To do this, I use this shebang:</p>
  311. <pre>
  312. #!/usr/bin/env -S uv run --script
  313. </pre>
  314. <p>The command line will now run <tt>uv run --script</tt> and pass the file as the argument. <tt>uv</tt> ignores the shebang and then runs the rest of the file as a normal Python file. </p>
  315. <p>Once I've ensured that that script has executable permissions via <tt>chmod a+x {filname}</tt>, I'm now good to go with simple command line scripts written in Python that automatically handle their dependencies!</p>
  316. ]]></content:encoded>
  317. <wfw:commentRss>https://akrabat.com/using-uv-as-your-shebang-line/feed/</wfw:commentRss>
  318. <slash:comments>2</slash:comments>
  319. </item>
  320. <item>
  321. <title>Always expand the Fantastical editor popover</title>
  322. <link>https://akrabat.com/always-expand-the-fantastical-editor-popover/</link>
  323. <comments>https://akrabat.com/always-expand-the-fantastical-editor-popover/#respond</comments>
  324. <dc:creator><![CDATA[Rob]]></dc:creator>
  325. <pubDate>Tue, 21 Jan 2025 11:00:00 +0000</pubDate>
  326. <category><![CDATA[Computing]]></category>
  327. <guid isPermaLink="false">https://akrabat.com/?p=7177</guid>
  328.  
  329. <description><![CDATA[My preferred calendar app for the Apple ecosystem is Fantastical as I've found that it meets my needs well. One minor irritant is that the editor popover defaults to a collapsed view and I have to expand it to see everything, in particular the notes field which I use frequently. I recently discovered that there's a hidden preference to change this. It's set via a custom url of x-fantastical3://defaults?key=AlwaysShowAll&#038;value=1&#038;type=bool&#038;group=1. Just click this link to set… <a href="https://akrabat.com/always-expand-the-fantastical-editor-popover/">continue reading</a>.]]></description>
  330. <content:encoded><![CDATA[<p>My preferred calendar app for the Apple ecosystem is <a href="https://flexibits.com/fantastical">Fantastical</a> as I've found that it meets my needs well. One minor irritant is that the editor popover defaults to a collapsed view and I have to expand it to see everything, in particular the notes field which I use frequently. </p>
  331. <p>I recently discovered that there's a hidden preference to change this. It's set via a custom url of <tt>x-fantastical3://defaults?key=AlwaysShowAll&value=1&type=bool&group=1</tt>. Just click this <a href="x-fantastical3://defaults?key=AlwaysShowAll&#038;value=1&#038;type=bool&#038;group=1">link to set</a> and it will open in Fantastical and change the setting.</p>
  332. <p>There's a helpful dialog to tell you what's going to happen too:<br />
  333. <img loading="lazy" decoding="async" src="https://akrabat.com/wp-content/uploads/2024/10/2024-10-24-fantastical-confimation.png" alt="" title="2024-10-24-fantastical-confimation.png" border="0" width="300" height="311" /></p>
  334. <p>Given that the Settings panel has an Advanced tab, you'd have thought that they could have put this in there.</p>
  335. <p>To reset, use this <a href="x-fantastical3://defaults?key=AlwaysShowAll&#038;value=0&#038;type=bool&#038;group=1">link to unset</a> which sets the value to zero.</p>
  336. ]]></content:encoded>
  337. <wfw:commentRss>https://akrabat.com/always-expand-the-fantastical-editor-popover/feed/</wfw:commentRss>
  338. <slash:comments>0</slash:comments>
  339. </item>
  340. <item>
  341. <title>Global git ignore patterns</title>
  342. <link>https://akrabat.com/global-git-ignore-patterns/</link>
  343. <comments>https://akrabat.com/global-git-ignore-patterns/#respond</comments>
  344. <dc:creator><![CDATA[Rob]]></dc:creator>
  345. <pubDate>Tue, 14 Jan 2025 11:00:00 +0000</pubDate>
  346. <category><![CDATA[Computing]]></category>
  347. <category><![CDATA[git]]></category>
  348. <guid isPermaLink="false">https://akrabat.com/?p=7220</guid>
  349.  
  350. <description><![CDATA[One thing that I've found helpful is to add a set of patterns to my global git ignore file (config/git/ignore for me) that allow me to create temporary files that are automatically excluded from git. The patterns I use are these: # Ignore a file by renaming it with ignore its name *.ignore ignore.* *.ignore.* This lets me create a file with a prefix ignore., a postfix of .ignore or add .ignore. somewhere in the… <a href="https://akrabat.com/global-git-ignore-patterns/">continue reading</a>.]]></description>
  351. <content:encoded><![CDATA[<p>One thing that I've found helpful is to add a set of patterns to my global <a href="https://git-scm.com/docs/gitignore"><tt>git ignore</tt></a> file (<tt>config/git/ignore</tt> for me) that allow me to create temporary files that are automatically excluded from git.</p>
  352. <p>The patterns I use are these:</p>
  353. <pre>
  354. # Ignore a file by renaming it with ignore its name
  355. *.ignore
  356. ignore.*
  357. *.ignore.*
  358. </pre>
  359. <p>This lets me create a file with a prefix <tt>ignore.</tt>, a postfix of <tt>.ignore</tt> or add <tt>.ignore.</tt> somewhere in the middle of the filename. This is particular useful for adding before the extension that's used for syntax detection in an editor, such as with <tt>.yaml</tt> files.</p>
  360. <p>It's a small thing, but so convenient when you want to keep a local file around for reference, but never accidentally commit it.</p>
  361. ]]></content:encoded>
  362. <wfw:commentRss>https://akrabat.com/global-git-ignore-patterns/feed/</wfw:commentRss>
  363. <slash:comments>0</slash:comments>
  364. </item>
  365. <item>
  366. <title>Enabling a focus mode when an app is running on Mac</title>
  367. <link>https://akrabat.com/enabling-a-focus-mode-when-an-app-is-running-on-mac/</link>
  368. <comments>https://akrabat.com/enabling-a-focus-mode-when-an-app-is-running-on-mac/#respond</comments>
  369. <dc:creator><![CDATA[Rob]]></dc:creator>
  370. <pubDate>Tue, 07 Jan 2025 11:00:00 +0000</pubDate>
  371. <category><![CDATA[Computing]]></category>
  372. <guid isPermaLink="false">https://akrabat.com/?p=7196</guid>
  373.  
  374. <description><![CDATA[When I'm on a Zoom or FaceTime call, I want stop all notifications on my Mac so that I'm not distracted by them and would like this automated. It's not easy to tell when a call is happening, so I simplified the problem to stopping all notifications if the Zoom or FaceTime is running as I only run these apps if I'm on call. To do this, I created two Shortcuts to turn the Do… <a href="https://akrabat.com/enabling-a-focus-mode-when-an-app-is-running-on-mac/">continue reading</a>.]]></description>
  375. <content:encoded><![CDATA[<p>When I'm on a Zoom or FaceTime call, I want stop all notifications on my Mac so that I'm not distracted by them and would like this automated.</p>
  376. <p>It's not easy to tell when a call is happening, so I simplified the problem to stopping all notifications if the Zoom or FaceTime is running as I only run these apps if I'm on call.</p>
  377. <p>To do this, I created two Shortcuts to turn the <em>Do Not Disturb</em> focus mode on and off: </p>
  378. <p><img loading="lazy" decoding="async" src="https://akrabat.com/wp-content/uploads/2024/11/shortcut-dnd-on.png" alt="Shortcut dnd on." title="shortcut-dnd-on.png" border="0" width="450" height="120" /></p>
  379. <p><img loading="lazy" decoding="async" src="https://akrabat.com/wp-content/uploads/2024/11/shortcut-dnd-off.png" alt="Shortcut dnd off." title="shortcut-dnd-off.png" border="0" width="450" height="118" /></p>
  380. <p>I then created two Keyboard Maestro macros:</p>
  381. <p><em>Video conferencing on</em>:<br />
  382. <img loading="lazy" decoding="async" src="https://akrabat.com/wp-content/uploads/2024/11/video-conferencing-on.png" alt="Video conferencing on." title="video-conferencing-on.png" border="0" width="499" height="423" /></p>
  383. <p><em>Video conferencing off</em>:<br />
  384. <img loading="lazy" decoding="async" src="https://akrabat.com/wp-content/uploads/2024/11/video-conferencing-off.png" alt="Video conferencing off." title="video-conferencing-off.png" border="0" width="499" height="426" /></p>
  385. <p>These look for the launching and quitting of the relevant apps and executes the shortcut. There's a minor issue that if I close one app if both apps are running, then DND is turned off, but I don't leave these apps open, so I'm not worried about it. If I was, I'd add additional logic or separate macros.</p>
  386. <p>Goal achieved! Whenever one of these apps are running (which is only when I'm on a call), the focus mode is enabled for me and is turned off again when I quit the app.</p>
  387. ]]></content:encoded>
  388. <wfw:commentRss>https://akrabat.com/enabling-a-focus-mode-when-an-app-is-running-on-mac/feed/</wfw:commentRss>
  389. <slash:comments>0</slash:comments>
  390. </item>
  391. </channel>
  392. </rss>
  393.  

If you would like to create a banner that links to this page (i.e. this validation result), do the following:

  1. Download the "valid RSS" banner.

  2. Upload the image to your own server. (This step is important. Please do not link directly to the image on this server.)

  3. Add this HTML to your page (change the image src attribute if necessary):

If you would like to create a text link instead, here is the URL you can use:

http://www.feedvalidator.org/check.cgi?url=http%3A//akrabat.com/feed/

Copyright © 2002-9 Sam Ruby, Mark Pilgrim, Joseph Walton, and Phil Ringnalda