<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>MacOS on Nelson Figueroa</title>
    <link>https://nelson.cloud/categories/macos/</link>
    <description>Recent content in MacOS on Nelson Figueroa</description>
    <image>
      <title>Nelson Figueroa</title>
      <url>https://nelson.cloud/opengraph-images/default.png</url>
      <link>https://nelson.cloud/opengraph-images/default.png</link>
    </image>
    <language>en</language>
    <lastBuildDate>Sun, 19 Apr 2026 00:32:00 -0700</lastBuildDate>
    <atom:link href="https://nelson.cloud/categories/macos/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>How to Install a Specific Version of a Homebrew Package with brew extract</title>
      <link>https://nelson.cloud/how-to-install-a-specific-version-of-a-homebrew-package-with-brew-extract/?ref=rss</link>
      <pubDate>Sun, 19 Apr 2026 00:00:00 +0000</pubDate>
      <guid>https://nelson.cloud/how-to-install-a-specific-version-of-a-homebrew-package-with-brew-extract/?ref=rss</guid>
      <description>Use &lt;code&gt;brew extract&lt;/code&gt; to install a specific version of a homebrew package.</description><content:encoded><![CDATA[<p>I previously wrote about 

<a href="https://nelson.cloud/how-to-install-older-versions-of-homebrew-packages/">how to install older versions of homebrew packages</a>. That method involves installing a package from a Ruby file but it&rsquo;s outdated and doesn&rsquo;t always work. There&rsquo;s a better way with <code>brew extract</code>, although it still comes with caveats.</p>
<p>I&rsquo;ll be using <code>hugo</code> as an example. Let&rsquo;s say I wanted to install v0.145.0 because v0.146.0 introduced breaking changes that broke my theme.</p>
<blockquote><p><strong>tl;dr:</strong></p><p>To install hugo v0.145.0:</p>
<ul>
<li>Create a local tap with <code>brew tap-new $USER/local</code></li>
<li>Tap homebrew/core which is a 1.3GB clone at the time of writing</li>
<li>Extract the formula with <code>brew extract</code></li>
<li>Patch the <code>hugo</code> formula. This isn&rsquo;t needed for every formula.</li>
<li>Install as usual</li>
</ul></blockquote>

<p>Note that this process will point your <code>hugo</code> command to the older version, but you can switch between versions with <code>brew link</code>.</p>
<h2 id="create-a-local-tap">Create a local tap</h2>
<p>Run <code>brew tap-new $USER/local</code></p>
<pre tabindex="0"><code>$ brew tap-new $USER/local

Initialized empty Git repository in /opt/homebrew/Library/Taps/nelson/homebrew-local/.git/
[main (root-commit) 6af371f] Create nelson/local tap
 3 files changed, 111 insertions(+)
 create mode 100644 .github/workflows/publish.yml
 create mode 100644 .github/workflows/tests.yml
 create mode 100644 README.md
==&gt; Created nelson/local
/opt/homebrew/Library/Taps/nelson/homebrew-local

When a pull request making changes to a formula (or formulae) becomes green
(all checks passed), then you can publish the built bottles.
To do so, label your PR as `pr-pull` and the workflow will be triggered.
</code></pre><p>It will enable developer mode. This is normal and safe.</p>
<h2 id="tap-homebrewcore">Tap homebrew/core</h2>
<p>Next, run <code>brew tap --force homebrew/core</code>. At the time of writing, it&rsquo;s a 1.3GB download. This is necessary to get this working because Homebrew no longer keeps 

<a href="https://github.com/homebrew/homebrew-core" target="_blank" rel="noopener">homebrew-core</a> cloned locally. The <code>brew extract</code> command needs the full git history to search for older versions.</p>
<pre tabindex="0"><code>$ brew tap --force homebrew/core

✔︎ JSON API formula.jws.json                                                                                                                                    Downloaded   32.0MB/ 32.0MB
✔︎ JSON API cask.jws.json                                                                                                                                       Downloaded   15.4MB/ 15.4MB
==&gt; Tapping homebrew/core
Cloning into &#39;/opt/homebrew/Library/Taps/homebrew/homebrew-core&#39;...
remote: Enumerating objects: 3385552, done.
remote: Counting objects: 100% (544/544), done.
remote: Compressing objects: 100% (119/119), done.
remote: Total 3385552 (delta 497), reused 427 (delta 425), pack-reused 3385008 (from 4)
Receiving objects: 100% (3385552/3385552), 1.08 GiB | 48.36 MiB/s, done.
Resolving deltas: 100% (2612327/2612327), done.
Tapped 5 commands and 8313 formulae (8,851 files, 1.3GB).
</code></pre><h2 id="extract-the-desired-version">Extract the desired version</h2>
<p>Now we can use <code>brew extract</code>. This command will find a commit where the formula was at the version we want and copy that locally as <code>&lt;package&gt;@&lt;version&gt;.rb</code>.</p>
<p>In this case we want Hugo v0.145.0, so we run <code>brew extract --version=0.145.0 hugo $USER/local</code>:</p>
<pre tabindex="0"><code>$ brew extract --version=0.145.0 hugo $USER/local

==&gt; Searching repository history
==&gt; Writing formula for hugo at 0.145.0 from revision a110fdb to:
/opt/homebrew/Library/Taps/nelson/homebrew-local/Formula/hugo@0.145.0.rb
</code></pre><h2 id="patch-the-formula">Patch the formula</h2>
<p>This isn&rsquo;t needed for every formula and is something I ran into specifically with Hugo. Without this patch, you&rsquo;ll run into errors.</p>
<p>After running <code>brew extract</code>, edit the file: <code>/opt/homebrew/Library/Taps/$USER/homebrew-local/Formula/hugo@0.145.0.rb</code>.</p>
<p>Change this line:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">system</span> <span class="s2">&#34;go&#34;</span><span class="p">,</span> <span class="s2">&#34;build&#34;</span><span class="p">,</span> <span class="o">*</span><span class="n">std_go_args</span><span class="p">(</span><span class="ss">ldflags</span><span class="p">:,</span> <span class="ss">tags</span><span class="p">:)</span>
</span></span></code></pre></td></tr></table>
</blockquote><p>To this:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">system</span> <span class="s2">&#34;go&#34;</span><span class="p">,</span> <span class="s2">&#34;build&#34;</span><span class="p">,</span> <span class="o">*</span><span class="n">std_go_args</span><span class="p">(</span><span class="ss">output</span><span class="p">:</span> <span class="n">bin</span><span class="o">/</span><span class="s2">&#34;hugo&#34;</span><span class="p">,</span> <span class="ss">ldflags</span><span class="p">:,</span> <span class="ss">tags</span><span class="p">:)</span>
</span></span></code></pre></td></tr></table>
</blockquote><p>The reason we need to patch this file is because it prevents the error:</p>
<pre tabindex="0"><code>command not found: /opt/homebrew/Cellar/hugo@0.145.0/0.145.0/bin/hugo
</code></pre><p>It&rsquo;s a mismatch between the path Homebrew expects (<code>bin/hugo</code>) vs the path that is created when using <code>brew extract</code> on Hugo (<code>bin/hugo@0.145.0</code>).</p>
<h2 id="install-the-older-version">Install the older version</h2>
<p>Now that Hugo is extracted and patched, we can install with <code>brew install hugo@0.145.0</code>:</p>
<pre tabindex="0"><code>$ brew install hugo@0.145.0

✔︎ JSON API cask.jws.json
✔︎ JSON API formula.jws.json
==&gt; Fetching downloads for: hugo@0.145.0
✔︎ Formula hugo@0.145.0 (0.145.0)
==&gt; Installing hugo@0.145.0 from nelson/local
==&gt; go build -o=/opt/homebrew/Cellar/hugo@0.145.0/0.145.0/bin/hugo -tags=extended withdeploy -ldflags=-s -w -X github.com/gohugoio/hugo/common/hugo.commitHash=nelson -X github.com/gohugoio/hugo/common/
==&gt; /opt/homebrew/Cellar/hugo@0.145.0/0.145.0/bin/hugo gen man --dir /opt/homebrew/Cellar/hugo@0.145.0/0.145.0/share/man/man1
Warning: These files were overwritten during the `brew link` step:
/opt/homebrew/etc/bash_completion.d/hugo
.
.
.
/opt/homebrew/share/zsh/site-functions/_hugo

They have been backed up to: /Users/nelson/Library/Caches/Homebrew/Backup
==&gt; Summary
🍺  /opt/homebrew/Cellar/hugo@0.145.0/0.145.0: 53 files, 73MB, built in 8 seconds
==&gt; Running `brew cleanup hugo@0.145.0`...
Disable this behaviour by setting `HOMEBREW_NO_INSTALL_CLEANUP=1`.
Hide these hints with `HOMEBREW_NO_ENV_HINTS=1` (see `man brew`).
==&gt; Caveats
zsh completions have been installed to:
  /opt/homebrew/share/zsh/site-functions
</code></pre><p>Hugo v0.145.0 is now installed. There&rsquo;s a warning with long output in the previous example due to the normal Hugo package being already installed but that is expected. Homebrew is now pointing the <code>hugo</code> binary to v0.145.0 instead of the latest version (v0.160.1 at the time of writing). We can verify with <code>hugo version</code>:</p>
<pre tabindex="0"><code>$ hugo version

hugo v0.145.0+extended+withdeploy darwin/arm64 BuildDate=2025-02-26T15:41:25Z VendorInfo=brew
</code></pre><p>We can also see that Hugo v0.145.0 is installed along with the latest version with <code>brew list | grep hugo</code>:</p>
<pre tabindex="0"><code>$ brew list | grep hugo

hugo
hugo@0.145.0
</code></pre><h2 id="switching-between-versions-with-brew-link">Switching Between Versions with <code>brew link</code></h2>
<p>Currently the <code>hugo</code> command is pointing to v0.145.0. To have it point back to the regular version, run <code>brew unlink hugo &amp;&amp; brew link --overwrite hugo</code>:</p>
<pre tabindex="0"><code>$ brew unlink hugo &amp;&amp; brew link --overwrite hugo

Unlinking /opt/homebrew/Cellar/hugo/0.160.1... 2 symlinks removed.
Linking /opt/homebrew/Cellar/hugo/0.160.1... 49 symlinks created.
</code></pre><p>And if we want <code>hugo</code> to point back to the old version, run <code>brew unlink hugo@0.145.0 &amp;&amp; brew link --overwrite hugo@0.145.0</code></p>
<pre tabindex="0"><code>$ brew unlink hugo@0.145.0 &amp;&amp; brew link --overwrite hugo@0.145.0

Unlinking /opt/homebrew/Cellar/hugo@0.145.0/0.145.0... 1 symlinks removed.
Linking /opt/homebrew/Cellar/hugo@0.145.0/0.145.0... 48 symlinks created.
</code></pre><p>At first I expected <code>brew link --overwrite hugo</code> to work right off the bat, but running both <code>brew unlink</code> and <code>brew link --overwrite</code> is necessary to switch between versions properly. This is because homebrew tracks linked formulas and actual symlinks on disk separately. To help Homebrew track things properly we need to run both <code>brew unlink</code> to clean the records, then <code>brew link --overwrite</code> to write the new symlinks.</p>
<p>There&rsquo;s no need to use <code>brew pin</code> to prevent the older version of Hugo from updating. Since this is a local copy, there is no remote repository that would be updated that would in turn update our local version. You can even try running <code>brew update</code> to see the warning message:</p>
<pre tabindex="0"><code>$ brew update

==&gt; Updating Homebrew...
Warning: No remote &#39;origin&#39; in /opt/homebrew/Library/Taps/nelson/homebrew-local, skipping update!
Already up-to-date.
</code></pre><h2 id="removing-the-older-version">Removing the Older Version</h2>
<p>If you no longer need Hugo v0.145.0 you can run <code>brew uninstall hugo@0.145.0</code>:</p>
<pre tabindex="0"><code>$ brew uninstall hugo@0.145.0

Uninstalling /opt/homebrew/Cellar/hugo@0.145.0/0.145.0... (53 files, 73MB)
</code></pre><p>If you don&rsquo;t have any other packages you extracted with <code>brew extract</code>, you can also remove your local tap with <code>brew untap $USER/local</code></p>
<pre tabindex="0"><code>$ brew untap $USER/local

Untapping nelson/local...
Untapped 1 formula (34 files, 36.7KB).
</code></pre><p>Finally, if you don&rsquo;t plan on using <code>brew extract</code> again in the future, you can remove the local clone of homebrew-core with <code>brew untap homebrew/core</code>. This will clean up the 1.3GB of files that was downloaded:</p>
<pre tabindex="0"><code>$ brew untap homebrew/core

Untapping homebrew/core...
Untapped 5 commands and 8313 formulae (8,975 files, 1.3GB).
</code></pre><p>Then re-link <code>hugo</code> to the latest version with <code>brew unlink hugo &amp;&amp; brew link hugo</code>:</p>
<pre tabindex="0"><code>$ brew unlink hugo &amp;&amp; brew link hugo

Unlinking /opt/homebrew/Cellar/hugo/0.160.1... 2 symlinks removed.
Linking /opt/homebrew/Cellar/hugo/0.160.1... 49 symlinks created.
</code></pre><h2 id="references">References</h2>
<ul>
<li>

<a href="https://docs.brew.sh/Manpage" target="_blank" rel="noopener">https://docs.brew.sh/Manpage</a></li>
<li>

<a href="https://github.com/orgs/Homebrew/discussions/2941" target="_blank" rel="noopener">https://github.com/orgs/Homebrew/discussions/2941</a></li>
<li>

<a href="https://emmer.dev/blog/installing-old-homebrew-formula-versions/" target="_blank" rel="noopener">https://emmer.dev/blog/installing-old-homebrew-formula-versions/</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Quick Tip: Mute the Terminal Login Message with A .hushlogin File</title>
      <link>https://nelson.cloud/quick-tip-mute-the-terminal-login-message-with-a-.hushlogin-file/?ref=rss</link>
      <pubDate>Sun, 28 Sep 2025 00:00:00 +0000</pubDate>
      <guid>https://nelson.cloud/quick-tip-mute-the-terminal-login-message-with-a-.hushlogin-file/?ref=rss</guid>
      <description>Create a .hushlogin file in your home directory to silence login messages.</description><content:encoded><![CDATA[<p>Today I learned that you can create an empty <code>.hushlogin</code> file in your home directory on macOS and Linux to hide the login message you get when starting up a new terminal window or tab.</p>
<p>I tried this on macOS but it should work on Linux too.</p>
<p>For example, when I start up a new terminal window/tab I see the following message:</p>
<img src="/mute-terminal-login-message/before.webp" alt="terminal with login message" width="980" height="254" style="max-width: 100%; height: auto; aspect-ratio: 980 / 254;" loading="lazy" decoding="async">
<p>After I create the <code>.hushlogin</code> file in my home directory, the login message goes away. First I created the file:</p>
<pre tabindex="0"><code>touch ~/.hushlogin
</code></pre><p>Then I opened a new terminal window to verify that the message no longer shows up:</p>
<img src="/mute-terminal-login-message/after.webp" alt="terminal without login message after creating .hushlogin file" width="980" height="254" style="max-width: 100%; height: auto; aspect-ratio: 980 / 254;" loading="lazy" decoding="async">
<h2 id="references">References</h2>
<ul>
<li>

<a href="https://stackoverflow.com/questions/15769615/remove-last-login-message-for-new-tabs-in-terminal" target="_blank" rel="noopener">https://stackoverflow.com/questions/15769615/remove-last-login-message-for-new-tabs-in-terminal</a></li>
<li>

<a href="https://www.cyberciti.biz/howto/turn-off-the-login-banner-in-linux-unix-with-hushlogin-file/" target="_blank" rel="noopener">https://www.cyberciti.biz/howto/turn-off-the-login-banner-in-linux-unix-with-hushlogin-file/</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Remove Shadows From Screenshots in macOS</title>
      <link>https://nelson.cloud/remove-shadows-from-screenshots-in-macos/?ref=rss</link>
      <pubDate>Sat, 05 Oct 2024 00:00:00 +0000</pubDate>
      <guid>https://nelson.cloud/remove-shadows-from-screenshots-in-macos/?ref=rss</guid>
      <description>Run this command to remove shadows from your screenshots in macOS: &lt;code&gt;defaults write com.apple.screencapture &amp;quot;disable-shadow&amp;quot; -bool &amp;quot;true&amp;quot;&lt;/code&gt;</description><content:encoded><![CDATA[<p>On macOS, screenshots of windows have an added drop shadow by default for whatever reason. Here&rsquo;s how to remove it:</p>
<p>To remove shadows from screenshots copy and paste this into the command line:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">defaults write com.apple.screencapture <span class="s2">&#34;disable-shadow&#34;</span> -bool <span class="s2">&#34;true&#34;</span>
</span></span></code></pre></td></tr></table>
</blockquote><br>
<p>To reset this setting and have shadows in your screenshots again run this:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">defaults delete com.apple.screencapture <span class="s2">&#34;disable-shadow&#34;</span>
</span></span></code></pre></td></tr></table>
</blockquote><br>
<p>Alternatively, you can press <code>command</code> + <code>shift</code> + <code>4</code> and then press <code>space</code>. Then hold <code>option</code> before you click on a window to take a screenshot. This removes the drop shadow.</p>
<h2 id="references">References</h2>
<ul>
<li>

<a href="https://www.reddit.com/r/MacOS/comments/q7h3xl/any_way_to_take_a_screenshot_on_mac_without_the/" target="_blank" rel="noopener">https://www.reddit.com/r/MacOS/comments/q7h3xl/any_way_to_take_a_screenshot_on_mac_without_the/</a></li>
<li>

<a href="https://www.idownloadblog.com/2014/08/03/how-to-remove-the-shadow-window-screenshots-on-mac-os-x/" target="_blank" rel="noopener">https://www.idownloadblog.com/2014/08/03/how-to-remove-the-shadow-window-screenshots-on-mac-os-x/</a></li>
</ul>
<p>There were several other sites covering this but they didn&rsquo;t get right to the point and/or they were ad-ridden, so I decided to write a straightforward post about this.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Find Unnecessary Homebrew Packages with &#34;brew leaves&#34;</title>
      <link>https://nelson.cloud/find-unnecessary-homebrew-packages-with-brew-leaves/?ref=rss</link>
      <pubDate>Mon, 22 Jan 2024 00:00:00 +0000</pubDate>
      <guid>https://nelson.cloud/find-unnecessary-homebrew-packages-with-brew-leaves/?ref=rss</guid>
      <description>Use the &amp;lsquo;brew leaves&amp;rsquo; command to find potentially unnecessary Homebrew packages.</description><content:encoded><![CDATA[<p>

<a href="https://brew.sh/" target="_blank" rel="noopener">Homebrew</a> has a 

<a href="https://docs.brew.sh/Manpage#leaves---installed-on-request---installed-as-dependency" target="_blank" rel="noopener">&ldquo;brew leaves&rdquo;</a> command that shows all installed packages that are not depended on by other packages. That means that they can be uninstalled without causing issues to other installed Homebrew packages. It&rsquo;s good to regularly run this command to keep Homebrew from getting too bloated.</p>
<p>Here&rsquo;s what my <code>brew leaves</code> output looks like at the time of this writing:</p>
<pre tabindex="0"><code>$ brew leaves

automake
bat
black
coreutils
ffmpeg
go
htop
hugo
jq
libksba
libpq
libtool
libyaml
node
openssl@1.1
pkg-config
postgresql@14
python-typing-extensions
tldr
tree
yt-dlp
zlib
</code></pre><p>In my case, I know I don&rsquo;t need the <code>libpq</code> package anymore. So I can remove this package and then run <code>brew leaves</code> again to confirm it&rsquo;s gone.</p>
<pre tabindex="0"><code>$ brew remove libpq

Uninstalling /opt/homebrew/Cellar/libpq/16.1_1... (2,380 files, 29.9MB)
</code></pre><pre tabindex="0"><code>$ brew leaves

automake
bat
black
coreutils
ffmpeg
go
htop
hugo
jq
libksba
libtool
libyaml
node
openssl@1.1
pkg-config
postgresql@14
python-typing-extensions
tldr
tree
yt-dlp
zlib
</code></pre><p>And now <code>libpq</code> is gone! Try out <code>brew leaves</code> yourself. You may be surprised at the amount of things installed that you may not actually need.</p>
<h2 id="references">References</h2>
<ul>
<li>

<a href="https://docs.brew.sh/Manpage#leaves---installed-on-request---installed-as-dependency" target="_blank" rel="noopener">https://docs.brew.sh/Manpage#leaves---installed-on-request---installed-as-dependency</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Delete All node_modules Directories Recursively in macOS and Linux</title>
      <link>https://nelson.cloud/delete-all-node_modules-directories-recursively-in-macos-and-linux/?ref=rss</link>
      <pubDate>Sun, 14 May 2023 00:00:00 +0000</pubDate>
      <guid>https://nelson.cloud/delete-all-node_modules-directories-recursively-in-macos-and-linux/?ref=rss</guid>
      <description>How to delete all node_modules directories recursively in macOS and Linux systems.</description><content:encoded><![CDATA[<p>We can use the <code>find</code> command to delete a specific directory recursively. This is the command formula:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">find /path/to/starting/directory/ -type d -name <span class="s2">&#34;directory_to_delete&#34;</span> -exec rm -rf <span class="o">{}</span> <span class="se">\;</span>
</span></span></code></pre></td></tr></table>
</blockquote><p>For example, if we wanted to delete all <code>node_modules</code> directories within the path <code>/projects/javascript/</code>, we would run:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">find /projects/javascript/ -type d -name <span class="s2">&#34;node_modules&#34;</span> -exec rm -rf <span class="o">{}</span> <span class="se">\;</span>
</span></span></code></pre></td></tr></table>
</blockquote><blockquote><p><strong>Note:</strong></p><p>Sometimes the output says:</p>
<pre tabindex="0"><code>find: ./node_modules: No such file or directory
</code></pre><p>But the command should still work.</p>
</blockquote>

<p>If you look closely, we&rsquo;re really just executing a command against all <code>node_modules</code> directories in the <code>/projects/javascript/</code> directory (<code>rm -rf</code>). This command can be modified to execute other commands against all <code>node_modules</code> directories, but that is out of the scope of this post :)</p>
]]></content:encoded>
    </item>
    <item>
      <title>How To Install Older Versions of Homebrew Packages</title>
      <link>https://nelson.cloud/how-to-install-older-versions-of-homebrew-packages/?ref=rss</link>
      <pubDate>Tue, 18 Apr 2023 00:00:00 +0000</pubDate>
      <guid>https://nelson.cloud/how-to-install-older-versions-of-homebrew-packages/?ref=rss</guid>
      <description>How to install a specific version of Homebrew packages.</description><content:encoded><![CDATA[<blockquote><p><strong>This method is outdated:</strong></p><p>The approach outlined in this post still works in some cases, but as Homebrew has changed, this method has become unreliable. You may run into errors like:</p>
<pre tabindex="0"><code>Error: Couldn&#39;t find manifest matching bottle checksum.
</code></pre><pre tabindex="0"><code>Error: An exception occurred within a child process:
  NoMethodError: undefined method &#39;user&#39; for nil
</code></pre><p>A cleaner approach is <code>brew extract</code>, which copies the historical formula into a local tap so Homebrew has the context it needs. See my newer post instead: 

<a href="https://nelson.cloud/how-to-install-a-specific-version-of-a-homebrew-package-with-brew-extract/" target="_blank" rel="noopener">How to Install a Specific Version of a Homebrew Package with brew extract</a>.</p>
</blockquote>

<p>It&rsquo;s possible to install older versions of Homebrew packages by saving an older version of the corresponding Ruby file locally and running <code>brew install &lt;package&gt;.rb</code>. I&rsquo;ll use the <code>terraform</code> package as an example.</p>
<blockquote><p><strong>tl;dr:</strong></p><p>If I wanted to downgrade to Terraform 1.3.6, I would need to:</p>
<ul>
<li>Find the Ruby file for that specific version of 

<a href="https://github.com/Homebrew/homebrew-core/blob/169f333f93fe0703b542cdf75b1decd4cb78f68d/Formula/terraform.rb" target="_blank" rel="noopener">Terraform on the Homebrew GitHub repo</a></li>
<li>Download the Ruby file</li>
<li>Uninstall the current version of terraform by running <code>brew remove terraform</code></li>
<li>Install the older version defined in the Ruby file by running <code>HOMEBREW_DEVELOPER=true brew install --formulae terraform.rb</code></li>
</ul></blockquote>

<p>Let&rsquo;s say we have <code>terraform</code> version 1.4.5 but we need <code>terraform</code> version 1.3.6. We can start by browsing to 

<a href="https://github.com/Homebrew/homebrew-core/tree/master/Formula" target="_blank" rel="noopener">https://github.com/Homebrew/homebrew-core/tree/master/Formula</a> and try to find the formula for <code>terraform</code> under the <code>t</code> directory.</p>
<img src="/how-to-install-older-versions-of-homebrew-packages/formulas.webp" alt="List of Homebrew formulas" width="720" height="410" style="max-width: 100%; height: auto; aspect-ratio: 3120 / 1780;" loading="lazy" decoding="async">
<p>Since there are a lot of files here, it&rsquo;s easier to just modify the URL path in the browser. Modify the path based on the directory the command is in: Take the name of the package and append it at the end of the url, adding <code>/&lt;directory-containing-package&gt;/&lt;package-name&gt;.rb</code> to the URL. The <code>.rb</code> is important because all Homebrew packages are defined in Ruby (files with the <code>.rb</code> extension).</p>
<p>In this case, we&rsquo;ll append <code>/t/terraform.rb</code> to the URL like so: 

<a href="https://github.com/Homebrew/homebrew-core/blob/master/Formula/t/terraform.rb" target="_blank" rel="noopener">https://github.com/Homebrew/homebrew-core/blob/master/Formula/t/terraform.rb</a></p>
<p>That URL will then take us to the Ruby file where the <code>terraform</code> Homebrew package is defined.</p>
<img src="/how-to-install-older-versions-of-homebrew-packages/terraform.webp" alt="Terraform Homebrew formula" width="720" height="410" style="max-width: 100%; height: auto; aspect-ratio: 3120 / 1780;" loading="lazy" decoding="async">
<p>Next, click the &ldquo;History&rdquo; link on the upper right above the code, or just click on this link 

<a href="https://github.com/Homebrew/homebrew-core/commits/master/Formula/t/terraform.rb" target="_blank" rel="noopener">https://github.com/Homebrew/homebrew-core/commits/master/Formula/t/terraform.rb</a>. In the next page, scroll down until you see the &ldquo;terraform: update 1.3.6 bottle&rdquo; link. Note that you may need to click on &ldquo;Browse History&rdquo; at the bottom of this page before continuing your search.</p>
<img src="/how-to-install-older-versions-of-homebrew-packages/1.3.6.webp" alt="Commit history for the terraform formula" width="720" height="410" style="max-width: 100%; height: auto; aspect-ratio: 3120 / 1780;" loading="lazy" decoding="async">
<p>Click on the 

<a href="https://github.com/Homebrew/homebrew-core/commit/169f333f93fe0703b542cdf75b1decd4cb78f68d" target="_blank" rel="noopener">terraform: update 1.3.6 bottle</a> link to see this page:</p>
<img src="/how-to-install-older-versions-of-homebrew-packages/commit.webp" alt="Commit for terraform 1.3.6" width="720" height="395" style="max-width: 100%; height: auto; aspect-ratio: 3840 / 2110;" loading="lazy" decoding="async">
<p>On the right side above the code block, click on the three dots, then click on &ldquo;View file&rdquo;.</p>
<img src="/how-to-install-older-versions-of-homebrew-packages/view-file.webp" alt="Three dots menu showing view file link" width="720" height="353" style="max-width: 100%; height: auto; aspect-ratio: 1872 / 918;" loading="lazy" decoding="async">
<p>This will take you to the 

<a href="https://github.com/Homebrew/homebrew-core/blob/169f333f93fe0703b542cdf75b1decd4cb78f68d/Formula/terraform.rb" target="_blank" rel="noopener">package formula for this specific version of terraform</a>.</p>
<img src="/how-to-install-older-versions-of-homebrew-packages/older-terraform.webp" alt="Formula for terraform 1.3.6" width="720" height="395" style="max-width: 100%; height: auto; aspect-ratio: 3840 / 2110;" loading="lazy" decoding="async">
<p>On the upper right side of the code block, click on &ldquo;Raw&rdquo;. This 

<a href="https://raw.githubusercontent.com/Homebrew/homebrew-core/169f333f93fe0703b542cdf75b1decd4cb78f68d/Formula/terraform.rb" target="_blank" rel="noopener">gives us the exact code we need</a> to install Terraform 1.3.6. Save the code locally to a file called <code>terraform.rb</code>. You can manually copy and paste or use <code>curl</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">curl https://raw.githubusercontent.com/Homebrew/homebrew-core/169f333f93fe0703b542cdf75b1decd4cb78f68d/Formula/terraform.rb &gt; terraform.rb
</span></span></code></pre></td></tr></table>
</blockquote><p>Then, remove the existing package:</p>
<pre tabindex="0"><code>$ brew remove terraform

Uninstalling /usr/local/Cellar/terraform/1.4.5... (6 files, 69MB)
</code></pre><p>Then run <code>brew install</code> but specify the file you saved locally to install the older version. Note that as of 

<a href="https://github.com/Homebrew/brew/releases/tag/4.6.4" target="_blank" rel="noopener">Homebrew version 4.6.4</a> you can no longer install formulae directly from a file. You can get around this by specifying 

<a href="https://github.com/Homebrew/brew/pull/20414" target="_blank" rel="noopener">the environment variable</a> <code>HOMEBREW_DEVELOPER</code>.</p>
<p>So to install Terraform from the file we just downloaded, run <code>HOMEBREW_DEVELOPER=true brew install --formulae terraform.rb</code>:</p>
<pre tabindex="0"><code>$ HOMEBREW_DEVELOPER=true brew install --formulae terraform.rb

==&gt; Fetching downloads for: terraform
✔︎ Bottle Manifest terraform (1.3.6)
✔︎ Bottle terraform (1.3.6)
Warning: terraform 1.5.7 is available and more recent than version 1.3.6.
==&gt; Pouring terraform--1.3.6.arm64_ventura.bottle.tar.gz
🍺  /opt/homebrew/Cellar/terraform/1.3.6: 7 files, 64MB
==&gt; Running `brew cleanup terraform`...
Disable this behaviour by setting `HOMEBREW_NO_INSTALL_CLEANUP=1`.
Hide these hints with `HOMEBREW_NO_ENV_HINTS=1` (see `man brew`).
</code></pre><p>Now <code>terraform</code> version 1.3.6 is installed!</p>
<pre tabindex="0"><code>$ terraform version

Terraform v1.3.6
on darwin_arm64
</code></pre><blockquote><p><strong>Pro Tip:</strong></p><p>You can pin the current version so it doesn&rsquo;t upgrade in the future.</p>
<pre tabindex="0"><code>brew pin terraform
</code></pre><p>The next time you run <code>brew upgrade</code> it will be skipped:</p>
<pre tabindex="0"><code>$ brew upgrade

Warning: Not upgrading 1 pinned package:
terraform 1.5.7
</code></pre><p>When you&rsquo;re ready to upgrade, you can unpin it:</p>
<pre tabindex="0"><code>brew unpin terraform
</code></pre></blockquote>

<h2 id="references">References</h2>
<ul>
<li>

<a href="https://docs.brew.sh/Manpage" target="_blank" rel="noopener">https://docs.brew.sh/Manpage</a></li>
<li>

<a href="https://docs.brew.sh/FAQ#how-do-i-stop-certain-formulae-from-being-updated" target="_blank" rel="noopener">https://docs.brew.sh/FAQ#how-do-i-stop-certain-formulae-from-being-updated</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Cleaning Up Residual Files on macOS After Deleting Apps</title>
      <link>https://nelson.cloud/cleaning-up-residual-files-on-macos-after-deleting-apps/?ref=rss</link>
      <pubDate>Sun, 28 Feb 2021 00:00:00 +0000</pubDate>
      <guid>https://nelson.cloud/cleaning-up-residual-files-on-macos-after-deleting-apps/?ref=rss</guid>
      <description>Clean up residual files and directories after deleting macOS apps.</description><content:encoded><![CDATA[<p>After deleting apps on macOS, they tend leave behind residual files and directories throughout the system. You can use the <code>find</code> command to find these files after an app has been deleted. I&rsquo;ll be deleting the LastPass app and removing its residual files as an example.</p>
<blockquote><p><strong>2024-10-13 Update:</strong></p><p>I recently discovered 

<a href="https://github.com/alienator88/Pearcleaner" target="_blank" rel="noopener">Pearcleaner</a>. You can use this app to delete other apps along with all the extra files and folders they create. I recommend you use this first and then continue reading this post if you want to look more deeply.</p>
<p>You can download Pearcleaner from the link above or install it with 

<a href="https://formulae.brew.sh/cask/pearcleaner" target="_blank" rel="noopener">Homebrew</a>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">brew install --cask pearcleaner
</span></span></code></pre></td></tr></table>
</blockquote>
</div>

<h2 id="searching-for-residual-files-and-directories">Searching for Residual Files and Directories</h2>
<p>To find directories and files related to LastPass, I ran a system-wide search using <code>find</code>. I excluded directories such as <code>/System/Volumes/Data</code> since those result in errors like &ldquo;Operation not permitted&rdquo;. I also excluded Homebrew directories that don&rsquo;t need to be cleaned up. You can add more directories as needed, just make sure to not add a trailing slash to the filepaths!</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">find / <span class="se">\
</span></span></span><span class="line"><span class="cl">-not <span class="se">\(</span> -path /System/Volumes/Data -prune <span class="se">\)</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">-not <span class="se">\(</span> -path /usr/local/Homebrew -prune <span class="se">\)</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">-not <span class="se">\(</span> -path /usr/local/Cellar -prune <span class="se">\)</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">-not <span class="se">\(</span> -path /usr/local/Caskroom -prune <span class="se">\)</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">-name <span class="se">\*</span>lastpass<span class="se">\*</span> 2&gt;<span class="p">&amp;</span><span class="m">1</span> <span class="p">|</span> grep -v -E <span class="s1">&#39;Operation not permitted|Permission denied|Not a directory&#39;</span>
</span></span></code></pre></td></tr></table>
</blockquote><p>The command may take some time to complete depending on your machine specs and amount of files you have. It took around ~10 minutes for me on a 2019 MacBook Pro with an i7 Intel CPU.</p>
<p>Here&rsquo;s the output I got from the command above. I can now go through each of these files and folders and decide if I want to delete them manually:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">/private/var/folders/m9/_7bg6tbn3636m1zzlzq33bwh0000gn/T/com.lastpass.lastpassmacdesktop
</span></span><span class="line"><span class="cl">/private/var/folders/m9/_7bg6tbn3636m1zzlzq33bwh0000gn/C/com.lastpass.lastpassmacdesktop
</span></span><span class="line"><span class="cl">/Users/nelson/Library/Application Support/com.lastpass.lastpassmacdesktop
</span></span><span class="line"><span class="cl">/Users/nelson/Library/WebKit/com.lastpass.lastpassmacdesktop
</span></span><span class="line"><span class="cl">/Users/nelson/Library/Preferences/com.lastpass.lastpassmacdesktop.plist
</span></span><span class="line"><span class="cl">/Users/nelson/Library/Application Scripts/N24REP3BMN.lmi.lastpass.group
</span></span><span class="line"><span class="cl">/Users/nelson/Library/Application Scripts/com.lastpass.lastpassmacdesktop.safariext
</span></span><span class="line"><span class="cl">/Users/nelson/Library/HTTPStorages/com.lastpass.lastpassmacdesktop.binarycookies
</span></span><span class="line"><span class="cl">/Users/nelson/Library/HTTPStorages/com.lastpass.lastpassmacdesktop
</span></span><span class="line"><span class="cl">/Users/nelson/Library/Group Containers/N24REP3BMN.lmi.lastpass.group
</span></span><span class="line"><span class="cl">/Users/nelson/Library/Group Containers/N24REP3BMN.lmi.lastpass.group/Library/Preferences/N24REP3BMN.lmi.lastpass.group.plist
</span></span><span class="line"><span class="cl">/Users/nelson/Library/Group Containers/N24REP3BMN.lmi.lastpass.group/Library/Application Scripts/N24REP3BMN.lmi.lastpass.group
</span></span><span class="line"><span class="cl">/Users/nelson/Library/Containers/com.lastpass.LastPass
</span></span><span class="line"><span class="cl">/Users/nelson/Library/Containers/com.lastpass.lastpassmacdesktop.safariext
</span></span><span class="line"><span class="cl">/Users/nelson/Library/Containers/com.lastpass.lastpassmacdesktop.safariext/Data/Library/Application Scripts/com.lastpass.lastpassmacdesktop.safariext
</span></span><span class="line"><span class="cl">/Users/nelson/Library/Caches/com.crashlytics.data/com.lastpass.lastpassmacdesktop
</span></span><span class="line"><span class="cl">/Users/nelson/Library/Caches/Homebrew/Cask/lastpass--4.116.0.dmg
</span></span><span class="line"><span class="cl">/Users/nelson/Library/Caches/com.lastpass.lastpassmacdesktop
</span></span></code></pre></td></tr></table>
</blockquote><p>This could probably be automated all the way through the deletion step, but I prefer to double check what is actually going to be deleted.</p>
<p>Also, this <em>may</em> help to free up space on macOS, but I mainly did it because I like keeping my system tidy.</p>
<p>Try this out with whatever apps you&rsquo;ve deleted in the past. You can also try finding files and folders be specifying a company name. For example, replace <code>\*lastpass\*</code> with <code>\*microsoft\*</code> and see what you get!</p>
<h2 id="references">References</h2>
<ul>
<li>

<a href="https://stackoverflow.com/questions/4210042/how-do-i-exclude-a-directory-when-using-find" target="_blank" rel="noopener">https://stackoverflow.com/questions/4210042/how-do-i-exclude-a-directory-when-using-find</a></li>
</ul>
]]></content:encoded>
    </item>
  </channel>
</rss>
