<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <atom:link href="https://nodewave-blogs.vercel.app/rss.xml" rel="self" type="application/rss+xml" />
        <title>My Nuxt 4 Blog</title>
        <link>https://nodewave-blogs.vercel.app/</link>
        <description>Latest insights, tutorials, and updates.</description>
        <lastBuildDate>Mon, 29 Jun 2026 17:24:55 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>Nuxt Content v3 RSS Generator</generator>
        <language>en</language>
        <copyright>All rights reserved 2026</copyright>
        <item>
            <title><![CDATA[Setting Up a PlatformIO Project with ESP-IDF - step-by-step Guide]]></title>
            <link>https://nodewave-blogs.vercel.app/blogs/platformio-esp-idf-setup</link>
            <guid isPermaLink="false">https://nodewave-blogs.vercel.app/blogs/platformio-esp-idf-setup</guid>
            <pubDate>Mon, 29 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Learn how to correctly scaffold, configure, and initialize an Espressif ESP-IDF project inside PlatformIO using both CLI tools and VS Code.]]></description>
            <content:encoded><![CDATA[<p>Microcontroller development moves fast. While the Arduino framework is great for rapid prototyping, production-grade hardware engineering often requires the raw optimization, power management features, and real-time operating system (FreeRTOS) integration native to Espressif’s official <strong>ESP-IDF</strong> (Espressif IoT Development Framework).</p>
<p>Managing toolchains directly inside ESP-IDF can be complex. This guide details how to build an isolated, reproducible development environment using <strong>PlatformIO</strong> to scaffold a project named <code>learn-gsm-esp32-dev</code>.</p>
<hr />
<h2 id="choosing-your-scaffolding-workflow">Choosing Your Scaffolding Workflow</h2>
<p>PlatformIO offers two separate pathways to initialize a new runtime: an automated command-line tool or an integrated graphical user interface (GUI) inside Visual Studio Code. Both paths yield identical configuration structures.</p>
<h3 id="option-a-the-rapid-cli-initialization">Option A: The Rapid CLI Initialization</h3>
<p>For developers working directly inside standard shells or automated scripting workflows, the PlatformIO Core CLI is the fastest tool available.</p>
<p>Execute this single string to build the folder structure, enter the directory, and invoke the scaffolding engine:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight"framework=espidf"
" style=""><code><span class="line" line="1"><span class="sBMFI">mkdir</span><span class="sfazB"> learn-gsm-esp32-dev</span><span class="sMK4o"> &&</span><span class="s2Zo4"> cd</span><span class="sfazB"> learn-gsm-esp32-dev</span><span class="sMK4o"> &&</span><span class="sBMFI"> pio</span><span class="sfazB"> project</span><span class="sfazB"> init</span><span class="sfazB"> --board</span><span class="sfazB"> esp32dev</span><span class="sfazB"> --project-option</span><span class="sMK4o"> "</span><span class="sfazB">framework=espidf</span><span class="sMK4o">"
</span></span></code></pre>
<h3 id="option-b-the-vs-code-visual-wizard">Option B: The VS Code Visual Wizard</h3>
<p>If you prefer visual management tools within your IDE, follow these manual configuration steps:</p>
<ol>
  <li>Open <strong>VS Code</strong>.</li>
  <li>Click the <strong>PlatformIO Alien Head icon</strong> on your primary sidebar activity deck.</li>
  <li>Locate the <strong>Quick Access</strong> panel and click <strong>PIO Home</strong> -&gt; <strong>Open</strong>.</li>
  <li>Click the <strong>New Project</strong> button on the central control page.</li>
  <li>
    Provide your project metrics within the wizard form:

    <ul>
      <li><strong>Name:</strong> <code>learn-gsm-esp32-dev</code></li>
      <li><strong>Board:</strong> Select <code>Espressif ESP32 Dev Module</code></li>
      <li><strong>Framework:</strong> Select <code>Espressif IoT Development Framework</code></li>
    </ul>
  </li>
  <li>Click <strong>Finish</strong> to pull down necessary core compiler tools and create the space.</li>
</ol>
<h2 id="navigating-the-component-file-structure">Navigating the Component File Structure</h2>
<p>ESP-IDF relies heavily on a rigid configuration structure orchestrated by CMake compilation rules. Unlike traditional flat Arduino sketch structures, your initialized directory needs to mirror this system layout layout:</p>
<pre class="[\"language-text\"]"><code>learn-gsm-esp32-dev/
├── include/
│   └── README
├── src/
│   ├── CMakeLists.txt
│   └── main.c
├── platformio.ini
└── CMakeLists.txt
</code></pre>
<hr />
<h2 id="configuring-the-system-manifest-files">Configuring the System Manifest Files</h2>
<p>To make sure your project compiles perfectly on your hardware target, overwrite your file structures using the standard boilerplate snippets below.</p>
<h3 id="_1-the-global-environment-manifest-platformioini">1. The Global Environment Manifest (<code>platformio.ini</code>)</h3>
<p>This root-level configuration defines how PlatformIO sets up its internal compilation pipelines, monitors outputs, and manages hardware flash targets.</p>
<pre class="language-ini shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span>[env:esp32dev]
</span></span><span class="line" line="2"><span>platform = espressif32
</span></span><span class="line" line="3"><span>board = esp32dev
</span></span><span class="line" line="4"><span>framework = espidf
</span></span><span class="line" line="5"><span>monitor_speed = 115200
</span></span></code></pre>
<h3 id="_2-root-build-instructions-cmakeliststxt">2. Root Build Instructions (<code>CMakeLists.txt</code>)</h3>
<p>This tells the base system build wrapper to locate your current ESP-IDF installation directory and load the required platform tools.</p>
<pre class="language-cmake shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span>cmake_minimum_required(VERSION 3.16.0)
</span></span><span class="line" line="2"><span>include(\$ENV{IDF_PATH}/tools/cmake/project.cmake)
</span></span><span class="line" line="3"><span>project(learn-gsm-esp32-dev)
</span></span></code></pre>
<h3 id="_3-component-build-targets-srccmakeliststxt">3. Component Build Targets (<code>src/CMakeLists.txt</code>)</h3>
<p>ESP-IDF views project folders as a series of modular items. This file registers your local code blocks as active targets for inclusion during compiler execution passes.</p>
<pre class="language-cmake shiki shiki-themes material-theme-lighter material-theme material-theme-palenight"main.c"
                    INCLUDE_DIRS ".")
" style=""><code><span class="line" line="1"><span>idf_component_register(SRCS "main.c"
</span></span><span class="line" line="2"><span>                    INCLUDE_DIRS ".")
</span></span></code></pre>
<h3 id="_4-hardware-boilerplate-logic-srcmainc">4. Hardware Boilerplate Logic (<code>src/main.c</code>)</h3>
<p>Unlike ordinary code loops, ESP-IDF relies directly on a non-returning execution target called <code>app_main</code>. This code implements simple periodic console logs using the native multitasking utility engine FreeRTOS.</p>
<pre class="language-c shiki shiki-themes material-theme-lighter material-theme material-theme-palenight"freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

static const char *TAG = "MAIN";

void app_main(void)
{
    ESP_LOGI(TAG, "GSM ESP32 Project Initialized");

    while (1) {
        ESP_LOGI(TAG, "Hello World from GSM Dev Board!");
        vTaskDelay(pd_TO_TICKS(2000));
    }
}
" style=""><code><span class="line" line="1"><span>#include &lt;stdio.h&gt;
</span></span><span class="line" line="2"><span>#include "freertos/FreeRTOS.h"
</span></span><span class="line" line="3"><span>#include "freertos/task.h"
</span></span><span class="line" line="4"><span>#include "esp_log.h"
</span></span><span class="line" line="5"><span emptyLinePlaceholder>
</span></span><span class="line" line="6"><span>static const char *TAG = "MAIN";
</span></span><span class="line" line="7"><span emptyLinePlaceholder>
</span></span><span class="line" line="8"><span>void app_main(void)
</span></span><span class="line" line="9"><span>{
</span></span><span class="line" line="10"><span>    ESP_LOGI(TAG, "GSM ESP32 Project Initialized");
</span></span><span class="line" line="11"><span emptyLinePlaceholder>
</span></span><span class="line" line="12"><span>    while (1) {
</span></span><span class="line" line="13"><span>        ESP_LOGI(TAG, "Hello World from GSM Dev Board!");
</span></span><span class="line" line="14"><span>        vTaskDelay(pd_TO_TICKS(2000));
</span></span><span class="line" line="15"><span>    }
</span></span><span class="line" line="16"><span>}
</span></span></code></pre>
<h2 id="compiling-and-running-code-on-your-device">Compiling and Running Code on Your Device</h2>
<p>With the project successfully configured, you can use these shortcuts to build and upload your code:</p>
<ul>
  <li><strong>Compile the Binaries:</strong> Hit <code>Ctrl + Alt + B</code> (Windows/Linux) or <code>Cmd + Option + B</code> (macOS).</li>
  <li><strong>Flash & Open Logging Monitor:</strong> Press <code>Ctrl + Alt + T</code> to open your terminal menu shell, then select the <strong>Upload and Monitor</strong> task option.</li>
</ul>
<p>Your terminal tool will begin tracking lines of standard serial log messages coming straight from your physical ESP32 device every two seconds.</p>
<style>
html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}
</style>]]></content:encoded>
            <author>no-reply@yourdomain.com (Gideon Yebei) (Gideon Yebei)</author>
        </item>
        <item>
            <title><![CDATA[Automate Your Release Notes - Building a Conventional Commit Changelog Hook]]></title>
            <link>https://nodewave-blogs.vercel.app/blogs/git-changelog-hook</link>
            <guid isPermaLink="false">https://nodewave-blogs.vercel.app/blogs/git-changelog-hook</guid>
            <pubDate>Mon, 29 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Stop writing release notes by hand. Learn how to pair Conventional Commits with a prepare-commit-msg hook to auto-generate crisp, readable project changelogs.]]></description>
            <content:encoded><![CDATA[<p>Writing release notes at the end of a sprint is a tedious chore that usually results in vague updates like <em>"Fixed bugs and updated files."</em> Your clients, project managers, and fellow developers deserve to know exactly what changed without scrolling through a messy git log history.</p>
<p>The solution isn't to work harder; it is to standardize your commit entries.</p>
<p>By pairing the <strong>Conventional Commits specification</strong> with automated Git hooks, your workspace can dynamically update a beautiful, hyper-accurate <code>CHANGELOG.md</code> file every time you tag a new software version.</p>
<h2 id="step-1-core-architecture-conventional-commits">Step 1: Core Architecture — Conventional Commits</h2>
<p>Automated changelogs only work if your computer can read and categorize your commit messages. Conventional Commits provide a strict structure that looks like this:</p>
<pre class="[\"language-text\"]"><code>&lt;type&gt;(&lt;optional scope&gt;): &lt;description&gt;

[optional body]
[optional footer(s)]
</code></pre>
<h3 id="common-types-feat-a-brand-new-application-feature-for-the-user">Common Types:- <code>feat:</code> A brand new application feature for the user.</h3>
<ul>
  <li><code>fix:</code> A bug resolution or patch.</li>
  <li><code>docs:</code> Documentation changes only.</li>
  <li><code>style:</code> Formatting, missing semicolons, or design updates (no code logic changes).</li>
  <li><code>refactor:</code> Code changes that neither fix a bug nor add a feature.</li>
</ul>
<h2 id="step-2-enforcing-the-standard-with-commitlint">Step 2: Enforcing the Standard with <code>commitlint</code></h2>
<p>Before we generate a changelog, we must block non-conforming commit messages. We can tie this verification step right into our existing <code>pre-commit</code> framework using <code>commitlint</code>.</p>
<ol>
  <li>Open your project's <code>.pre-commit-config.yaml</code> file.</li>
  <li>Append the following block to install the validation engine:</li>
</ol>
<pre class="language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight"@commitlint/config-conventional"]
" style=""><code><span class="line" line="1"><span class="sMK4o">-</span><span class="swJcz"> repo</span><span class="sMK4o">:</span><span class="sfazB"> https://github.com
</span></span><span class="line" line="2"><span emptyLinePlaceholder>
</span></span><span class="line" line="3"><span class="swJcz">  rev</span><span class="sMK4o">:</span><span class="sfazB"> v9.16.0
</span></span><span class="line" line="4"><span class="swJcz">  hooks</span><span class="sMK4o">:
</span></span><span class="line" line="5"><span class="sMK4o">    -</span><span class="swJcz"> id</span><span class="sMK4o">:</span><span class="sfazB"> commitlint
</span></span><span class="line" line="6"><span class="swJcz">      stages</span><span class="sMK4o">:</span><span class="sMK4o"> [</span><span class="sfazB">commit-msg</span><span class="sMK4o">]
</span></span><span class="line" line="7"><span class="swJcz">      additional_dependencies</span><span class="sMK4o">:</span><span class="sMK4o"> [</span><span class="sMK4o">"</span><span class="sfazB">@commitlint/config-conventional</span><span class="sMK4o">"</span><span class="sMK4o">]
</span></span></code></pre>
<ol start="3">
  <li>Create a configuration file named <code>commitlint.config.js</code> in your root folder to load the standard definitions:</li>
</ol>
<pre class="language-javascript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight"@commitlint/config-conventional"] };
" style=""><code><span class="line" line="1"><span>module.exports = { extends: ["@commitlint/config-conventional"] };
</span></span></code></pre>
<ol start="4">
  <li>Register the message hook with your local Git subsystem:</li>
</ol>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">pre-commit</span><span class="sfazB"> install</span><span class="sfazB"> --hook-type</span><span class="sfazB"> commit-msg
</span></span></code></pre>
<p>Now, if you try to type a lazy message like <code>git commit -m "fixed stuff"</code>, your terminal will promptly reject it, forcing your message structure to stay uniform.</p>
<h2 id="step-3-setting-up-the-automated-changelog-pipeline">Step 3: Setting Up the Automated Changelog Pipeline</h2>
<p>To extract these standardized logs into a markdown file, we will use <strong>Commitizen</strong> and <strong>Standard-Version</strong> (or its modern equivalent, <code>cliff-or-cz</code>). For maximum flexibility across any development environment, we will use a lightweight Python tool named <code>cz-cli</code> or Node-based <code>standard-version</code>.</p>
<p>Let's configure it via NPM for general engineering environments:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">npm</span><span class="sfazB"> install</span><span class="sfazB"> -g</span><span class="sfazB"> standard-version
</span></span></code></pre>
<p>Add an execution shortcut script to your project's <code>package.json</code> file:</p>
<pre class="language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight"scripts": {
    "release": "standard-version"
  }
}
" style=""><code><span class="line" line="1"><span class="sMK4o">{
</span></span><span class="line" line="2"><span class="sMK4o">  "</span><span class="spNyl">scripts</span><span class="sMK4o">"</span><span class="sMK4o">:</span><span class="sMK4o"> {
</span></span><span class="line" line="3"><span class="sMK4o">    "</span><span class="sBMFI">release</span><span class="sMK4o">"</span><span class="sMK4o">:</span><span class="sMK4o"> "</span><span class="sfazB">standard-version</span><span class="sMK4o">"
</span></span><span class="line" line="4"><span class="sMK4o">  }
</span></span><span class="line" line="5"><span class="sMK4o">}
</span></span></code></pre>
<p>When you execute your new command, the runner automates a multi-step workflow:</p>
<ol>
  <li>It reviews all commit logs since your last tag.</li>
  <li>It bumps your project version number following semantic versioning rules (<code>v1.0.0</code> -&gt; <code>v1.1.0</code>).</li>
  <li>It generates or updates your <code>CHANGELOG.md</code> file dynamically.</li>
  <li>It creates a local git tag for the new release.</li>
</ol>
<h2 id="step-4-the-post-commit-automated-hook">Step 4: The Post-Commit Automated Hook</h2>
<p>If you want the changelog update process to trigger entirely behind the scenes whenever a release or merge happens, you can link it directly into your git workflow using a <code>post-commit</code> hook.</p>
<p>Create a file named <code>.git/hooks/post-merge</code> (or configure it in your pipeline tools):</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight"chore(release):"; then
    echo "Release commit detected. Pushing updated documentation..."
    git push --follow-tags origin main
fi
" style=""><code><span class="line" line="1"><span class="sHwdD">#!/bin/bash
</span></span><span class="line" line="2"><span class="sHwdD"># Check if the last commit was a formal release version switch
</span></span><span class="line" line="3"><span class="s7zQu">if</span><span class="sBMFI"> git</span><span class="sfazB"> log</span><span class="sfazB"> -1</span><span class="sfazB"> --pretty=%B</span><span class="sMK4o"> |</span><span class="sBMFI"> grep</span><span class="sfazB"> -q</span><span class="sMK4o"> "</span><span class="sfazB">chore(release):</span><span class="sMK4o">"</span><span class="sMK4o">;</span><span class="s7zQu"> then
</span></span><span class="line" line="4"><span class="s2Zo4">    echo</span><span class="sMK4o"> "</span><span class="sfazB">Release commit detected. Pushing updated documentation...</span><span class="sMK4o">"
</span></span><span class="line" line="5"><span class="sBMFI">    git</span><span class="sfazB"> push</span><span class="sfazB"> --follow-tags</span><span class="sfazB"> origin</span><span class="sfazB"> main
</span></span><span class="line" line="6"><span class="s7zQu">fi
</span></span></code></pre>
<h3 id="the-end-result-your-new-changelogmd">The End Result: Your New <code>CHANGELOG.md</code></h3>
<p>The pipeline aggregates your individual commits and transforms them instantly into clean markdown:</p>
<h2 id="_120-2026-06-29">1.2.0 (2026-06-29)</h2>
<h3 id="features">Features</h3>
<ul>
  <li><strong>auth:</strong> added interactive two-factor authentication flow (<span>a1b2c3d</span>)</li>
  <li><strong>terminal:</strong> injected carapace subcommand engine (<span>e5f6g7h</span>)</li>
</ul>
<h3 id="bug-fixes">Bug Fixes</h3>
<ul>
  <li><strong>profile:</strong> resolved broken path lookup inside windows systems (<span>z9y8x7w</span>)</li>
</ul>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>By delegating your documentation to a structured commit linting framework, you completely delete release coordination overhead. Your code history stays searchable, your changes remain organized, and your project stakeholders get crisp release descriptions updated completely on autopilot.</p>
<style>
html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}
</style>]]></content:encoded>
            <author>no-reply@yourdomain.com (Gideon Yebei) (Gideon Yebei)</author>
        </item>
        <item>
            <title><![CDATA[Getting Started with ESP32 - A Comprehensive Guide to Installing ESP-IDF on Ubuntu]]></title>
            <link>https://nodewave-blogs.vercel.app/blogs/esp-idf-ubuntu-setup</link>
            <guid isPermaLink="false">https://nodewave-blogs.vercel.app/blogs/esp-idf-ubuntu-setup</guid>
            <pubDate>Mon, 29 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Master the complete installation workflow for Espressif SoCs on Ubuntu. Learn to configure dependencies, purge line-ending bugs, compile firmware, and manage ports.]]></description>
            <content:encoded><![CDATA[<p>Setting up an embedded development ecosystem on Linux shouldn't feel like guesswork. While Espressif's IoT Development Framework (ESP-IDF) provides massive flexibility for targeting microcontrollers like the standard ESP32, ESP32-S3, or ESP32-C3, configuring the native tools requires a precise sequence of system dependency curation and shell workspace preparation.</p>
<p>This comprehensive guide details the absolute path to setting up a production-ready ESP-IDF development environment natively on an Ubuntu system, including fixes for line endings (CRLF), peripheral port configurations, and shell environment persistence.</p>
<h2 id="preparing-host-tools-and-dependencies">Preparing Host Tools and Dependencies</h2>
<p>The ESP-IDF toolchain requires specific host utility packages—ranging from build engines like CMake and Ninja to cross-compilation system modules. Update your internal package paths first using the <a href="https://ubuntu.com/server/docs/package-management" rel="[\"nofollow\"]">Advanced Package Tool (APT)</a>:</p>
<h3 id="refresh-system-software-index-repositories">Refresh system software index repositories</h3>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">sudo</span><span class="sfazB"> apt</span><span class="sfazB"> update
</span></span></code></pre>
<h3 id="install-required-build-tools-python-environments-and-underlying-usb-port-managers">Install required build tools, Python environments, and underlying USB port managers</h3>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">sudo</span><span class="sfazB"> apt</span><span class="sfazB"> install</span><span class="sfazB"> -y</span><span class="sfazB"> git</span><span class="sfazB"> wget</span><span class="sfazB"> flex</span><span class="sfazB"> bison</span><span class="sfazB"> gperf</span><span class="sfazB"> python3</span><span class="sfazB"> python3-pip</span><span class="sfazB"> python3-venv</span><span class="sTEyZ"> \
</span></span><span class="line" line="2"><span class="sTEyZ">cmake </span><span class="sfazB">ninja-build</span><span class="sfazB"> ccache</span><span class="sfazB"> libffi-dev</span><span class="sfazB"> libssl-dev</span><span class="sfazB"> dfu-util</span><span class="sfazB"> libusb-1.0-0</span><span class="sfazB"> dos2unix
</span></span></code></pre>
<p>Ensure your Python installation is functional before proceeding:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">python3</span><span class="sfazB"> --version
</span></span></code></pre>
<h2 id="cloning-the-target-code-repository">Cloning the Target Code Repository</h2>
<p>Create a structured development folder inside your user home directory to house the framework workspace. To keep things highly compatible across active projects, clone the repository while explicitly retaining structural submodules:</p>
<h3 id="form-the-core-directory-path">Form the core directory path</h3>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">mkdir</span><span class="sfazB"> -p</span><span class="sfazB"> ~/esp
</span></span><span class="line" line="2"><span class="s2Zo4">cd</span><span class="sfazB"> ~/esp
</span></span></code></pre>
<h3 id="clone-the-main-repository-framework-into-a-local-folder-named-espidf">Clone the main repository framework into a local folder named espidf</h3>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">git</span><span class="sfazB"> clone</span><span class="sfazB"> --recursive</span><span class="sfazB"> https://github.com/espressif/esp-idf.git</span><span class="sfazB"> espidf
</span></span></code></pre>
<h3 id="the-broken-submodule-catch-all">The Broken Submodule Catch-All</h3>
<p>If you previously pulled down the project files but skipped the submodule dependencies during initialization, your local repository tree will sit in an incomplete state. Repair it instantly by running:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="s2Zo4">cd</span><span class="sfazB"> ~/esp/espidf
</span></span><span class="line" line="2"><span class="sBMFI">git</span><span class="sfazB"> submodule</span><span class="sfazB"> update</span><span class="sfazB"> --init</span><span class="sfazB"> --recursive
</span></span></code></pre>
<h3 id="purging-cross-platform-shebang-line-issues">Purging Cross-Platform Shebang Line Issues</h3>
<p>If your repository configurations pass through host file servers or a Windows subsystem before arriving on your Ubuntu box, line endings can easily convert from Unix LF format to Windows CRLF formats. This hidden alteration triggers terminal script rejections like:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">usr/bin/env:</span><span class="sfazB"> ‘bash</span><span class="sTEyZ">\r</span><span class="sfazB">’:</span><span class="sfazB"> No</span><span class="sfazB"> such</span><span class="sfazB"> file</span><span class="sfazB"> or</span><span class="sfazB"> directory
</span></span></code></pre>
<p>Fix this instantly across your whole localized repository tree before calling any internal installer scripts:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight"*.sh" -exec dos2unix {} +
" style=""><code><span class="line" line="1"><span class="s2Zo4">cd</span><span class="sfazB"> ~/esp/espidf
</span></span><span class="line" line="2"><span emptyLinePlaceholder>
</span></span><span class="line" line="3"><span class="sHwdD">### Force any corrupted shell scripts back into proper native Unix layout parameters
</span></span><span class="line" line="4"><span class="sBMFI">find</span><span class="sfazB"> .</span><span class="sfazB"> -name</span><span class="sMK4o"> "</span><span class="sfazB">*.sh</span><span class="sMK4o">"</span><span class="sfazB"> -exec</span><span class="sfazB"> dos2unix</span><span class="sfazB"> {}</span><span class="sfazB"> +
</span></span></code></pre>
<h3 id="triggering-the-toolchain-installation">Triggering the Toolchain Installation</h3>
<p>With clean formatting secured, execute the structural installation script. While you can opt to install tool sets for every single target variant via <code>./install.sh all</code>, targeting only your explicit board framework saves hours of disk read times and download space.</p>
<p>For standard boards such as the ESP32-DevKitC (esp32dev), pass the esp32 parameter directly:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="s2Zo4">cd</span><span class="sfazB"> ~/esp/espidf
</span></span><span class="line" line="2"><span class="sBMFI">./install.sh</span><span class="sfazB"> esp32
</span></span></code></pre>
<p>This script builds an isolated Python environment and populates custom cross-compilation binaries (such as xtensa-esp32-elf) natively within a hidden directory layout (<code>~/.espressif</code>).</p>
<h2 id="mapping-the-active-shell-workspace">Mapping the Active Shell Workspace</h2>
<p>The environment tools are completely isolated by design to prevent conflicting with regular Linux operations. Consequently, you must load the active tool configuration paths into your shell workspace session before compilation.</p>
<h3 id="source-the-configuration-environment-map-directly">Source the configuration environment map directly</h3>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="s2Zo4">cd</span><span class="sfazB"> ~/esp/espidf
</span></span><span class="line" line="2"><span class="s2Zo4">.</span><span class="sfazB"> ./export.sh
</span></span></code></pre>
<h4 id="establishing-a-permanent-environment-shortcut">Establishing a Permanent Environment Shortcut</h4>
<p>Running that explicit script string manually every time you launch a fresh Ubuntu terminal window is tedious. Simplify your pathing by introducing a terminal profile alias:</p>
<ol>
  <li>Open your native terminal profile configuration:</li>
</ol>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI"> nano</span><span class="sfazB"> ~/.bashrc
</span></span></code></pre>
<ol start="2">
  <li>Paste this macro line at the absolute bottom of the document space:</li>
</ol>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="spNyl">alias</span><span class="sTEyZ"> get_idf</span><span class="sMK4o">=</span><span class="sMK4o">'</span><span class="sfazB">. ~/esp/espidf/export.sh</span><span class="sMK4o">'
</span></span></code></pre>
<ol start="3">
  <li>
    Save the changes and exit (<kbd value="meta"></kbd> + <kbd value="O"></kbd> <kbd value="Enter"></kbd> <kbd value="Ctrl"></kbd> + <kbd value="X"></kbd>).
  </li>
</ol>
<p>Now, entering the lightweight command <code>get_idf</code> inside any new bash terminal session instantly prepares your environment workspace.</p>
<h2 id="project-compilation-flashing-and-port-access">Project Compilation, Flashing, and Port Access</h2>
<p>With your environment loaded cleanly, test the complete pipeline by spinning up a fresh workspace program outside of your framework code repository path:</p>
<h3 id="copy-a-foundational-template-application-to-your-workspace">Copy a foundational template application to your workspace</h3>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">cp</span><span class="sfazB"> -r</span><span class="sfazB"> ~/esp/espidf/examples/get-started/hello_world</span><span class="sfazB"> ~/esp/
</span></span><span class="line" line="2"><span class="s2Zo4">cd</span><span class="sfazB"> ~/esp/hello_world
</span></span><span class="line" line="3"><span class="sHwdD">## Clean up any lingering or corrupted configuration file configurations
</span></span><span class="line" line="4"><span class="sBMFI">rm</span><span class="sfazB"> -rf</span><span class="sfazB"> build/
</span></span><span class="line" line="5"><span emptyLinePlaceholder>
</span></span><span class="line" line="6"><span class="sHwdD">## Target your hardware profile layout parameters
</span></span><span class="line" line="7"><span class="sBMFI">idf.py</span><span class="sfazB"> set-target</span><span class="sfazB"> esp32
</span></span></code></pre>
<h3 id="managing-system-port-permissions">Managing System Port Permissions</h3>
<p>When you plug your ESP32 board into a USB port on Ubuntu, the kernel creates devices like <code>/dev/ttyUSB0</code> or <code>/dev/ttyACM0</code>.</p>
<p>However, standard Linux users lack read/write permissions for these devices by default, causing flashing attempts to fail with a Permission Denied error.</p>
<p>Grant your active system user profile permanent access privileges:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">sudo</span><span class="sfazB"> usermod</span><span class="sfazB"> -aG</span><span class="sfazB"> dialout</span><span class="sTEyZ"> $USER
</span></span></code></pre>
<note>
  <p>You must log out of your Ubuntu user session completely and log back in for this access mapping to apply to your terminal terminal.</p>
</note>
<h3 id="compiling-flashing-and-monitoring">Compiling, Flashing, and Monitoring</h3>
<p>Chain your final terminal steps together to execute compilation, flash binaries directly to your hardware, and capture serial terminal output in real time:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">idf.py</span><span class="sfazB"> build</span><span class="sfazB"> flash</span><span class="sfazB"> monitor
</span></span></code></pre>
<p>
  To exit the real-time serial monitor window safely at any time, press <kbd value="Ctrl"></kbd> + <kbd value="]"></kbd>.
</p>
<style>
html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}
</style>]]></content:encoded>
            <author>no-reply@yourdomain.com (Gideon Yebei) (Gideon Yebei)</author>
        </item>
        <item>
            <title><![CDATA[Supercharge Your Git Workflow - Automate Quality Control with Pre-Commit Hooks]]></title>
            <link>https://nodewave-blogs.vercel.app/blogs/git-pre-commit</link>
            <guid isPermaLink="false">https://nodewave-blogs.vercel.app/blogs/git-pre-commit</guid>
            <pubDate>Sun, 28 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Stop pushing broken code or accidental API keys. Learn how to configure a multi-language pre-commit pipeline that scans your staged files completely offline.]]></description>
            <content:encoded><![CDATA[<p>We have all done it. You type a fast <code>git commit -m "fix typo"</code>, push it straight to production, and immediately break the automated CI/CD pipeline because you left a missing trailing semicolon, a broken bracket, or worse—a raw, unencrypted API key.</p>
<p>Instead of relying on remote servers to find your silly mistakes, you can force your local machine to check your work first.</p>
<p>By leveraging <strong>Pre-Commit Hooks</strong>, Git will automatically run your code through a gauntlet of formatters, linters, and security scanners. If anything is broken, it blocks the commit right on your desktop before it can ever infect your shared repository history.</p>
<h2 id="step-1-why-built-in-git-hooks-are-clunky">Step 1: Why Built-In Git Hooks Are Clunky</h2>
<p>If you look inside any project workspace directory under <code>.git/hooks/</code>, you will see a collection of sample shell scripts. You <em>could</em> write raw Bash scripts inside <code>.git/hooks/pre-commit</code> directly, but this approach has two massive problems:</p>
<ol>
  <li><strong>Non-Transferable:</strong> The <code>.git/</code> folder is strictly ignored by your project history. Your team cannot download or share your custom hook rules.2. <strong>Maintenance Nightmare:</strong> Writing custom logic to isolate only changed lines, handle multiple languages, and format files safely takes hundreds of lines of brittle bash syntax.</li>
</ol>
<p>To fix this, we use the industry-standard <strong>pre-commit framework</strong>. It abstracts away the complex logic into a single, clean configuration file.</p>
<h2 id="step-2-installing-the-pre-commit-framework">Step 2: Installing the Pre-Commit Framework</h2>
<p>First, install the pipeline manager tool using your favorite system package manager:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sHwdD"># macOS (Homebrew)
</span></span><span class="line" line="2"><span class="sBMFI">brew</span><span class="sfazB"> install</span><span class="sfazB"> pre-commit
</span></span><span class="line" line="3"><span emptyLinePlaceholder>
</span></span><span class="line" line="4"><span class="sHwdD"># Windows (Winget or Pip)
</span></span><span class="line" line="5"><span class="sBMFI">winget</span><span class="sfazB"> install</span><span class="sfazB"> pre-commit
</span></span><span class="line" line="6"><span class="sHwdD"># OR: pip install pre-commit
</span></span></code></pre>
<p>Verify that the installation was successful by checking the version string:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">pre-commit</span><span class="sfazB"> --version
</span></span></code></pre>
<h2 id="step-3-architecting-your-multi-language-blueprint">Step 3: Architecting Your Multi-Language Blueprint</h2>
<p>Navigate to the root directory of your project repository and create a brand new configuration file named <code>.pre-commit-config.yaml</code>:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">touch</span><span class="sfazB"> .pre-commit-config.yaml
</span></span></code></pre>
<p>Open this new file and paste the following high-utility core structure. This layout handles standard code hygiene, layout formatting, and automated security scans all at once:</p>
<pre class="language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sHwdD"># See https://pre-commit.com for more information
</span></span><span class="line" line="2"><span class="sHwdD"># See https://pre-commit.com for more community hooks
</span></span><span class="line" line="3"><span emptyLinePlaceholder>
</span></span><span class="line" line="4"><span class="swJcz">repos</span><span class="sMK4o">:
</span></span><span class="line" line="5"><span class="sHwdD">  # 1. Standard Code Hygiene & Cleanup
</span></span><span class="line" line="6"><span class="sMK4o">  -</span><span class="swJcz"> repo</span><span class="sMK4o">:</span><span class="sfazB"> https://github.com
</span></span><span class="line" line="7"><span emptyLinePlaceholder>
</span></span><span class="line" line="8"><span class="swJcz">    rev</span><span class="sMK4o">:</span><span class="sfazB"> v4.6.0</span><span class="sHwdD"> # Use the latest stable version
</span></span><span class="line" line="9"><span class="swJcz">    hooks</span><span class="sMK4o">:
</span></span><span class="line" line="10"><span class="sMK4o">      -</span><span class="swJcz"> id</span><span class="sMK4o">:</span><span class="sfazB"> trailing-whitespace</span><span class="sHwdD"> # Trims unnecessary spaces at the end of lines
</span></span><span class="line" line="11"><span class="sMK4o">      -</span><span class="swJcz"> id</span><span class="sMK4o">:</span><span class="sfazB"> end-of-file-fixer</span><span class="sHwdD"> # Ensures files end with a standard newline character
</span></span><span class="line" line="12"><span class="sMK4o">      -</span><span class="swJcz"> id</span><span class="sMK4o">:</span><span class="sfazB"> check-yaml</span><span class="sHwdD"> # Validates structural syntax of all YAML files
</span></span><span class="line" line="13"><span class="sMK4o">      -</span><span class="swJcz"> id</span><span class="sMK4o">:</span><span class="sfazB"> check-added-large-files</span><span class="sHwdD"> # Blocks giant files from accidentally bloating the repo
</span></span><span class="line" line="14"><span emptyLinePlaceholder>
</span></span><span class="line" line="15"><span class="sHwdD">  # 2. Security: Stop Secret & Credential Leaking
</span></span><span class="line" line="16"><span class="sMK4o">  -</span><span class="swJcz"> repo</span><span class="sMK4o">:</span><span class="sfazB"> https://github.com
</span></span><span class="line" line="17"><span emptyLinePlaceholder>
</span></span><span class="line" line="18"><span class="swJcz">    rev</span><span class="sMK4o">:</span><span class="sfazB"> v8.18.2
</span></span><span class="line" line="19"><span class="swJcz">    hooks</span><span class="sMK4o">:
</span></span><span class="line" line="20"><span class="sMK4o">      -</span><span class="swJcz"> id</span><span class="sMK4o">:</span><span class="sfazB"> gitleaks-system</span><span class="sHwdD"> # Scans staged lines for AWS, Stripe, or GitHub API tokens
</span></span><span class="line" line="21"><span emptyLinePlaceholder>
</span></span><span class="line" line="22"><span class="sHwdD">  # 3. Code Formatting (Example: Python/Web Assets)
</span></span><span class="line" line="23"><span class="sMK4o">  -</span><span class="swJcz"> repo</span><span class="sMK4o">:</span><span class="sfazB"> https://github.com
</span></span><span class="line" line="24"><span emptyLinePlaceholder>
</span></span><span class="line" line="25"><span class="swJcz">    rev</span><span class="sMK4o">:</span><span class="sbssI"> 24.4.2
</span></span><span class="line" line="26"><span class="swJcz">    hooks</span><span class="sMK4o">:
</span></span><span class="line" line="27"><span class="sMK4o">      -</span><span class="swJcz"> id</span><span class="sMK4o">:</span><span class="sfazB"> black</span><span class="sHwdD"> # Instantly formats Python files to strict style guides
</span></span></code></pre>
<h2 id="step-4-activating-your-local-shield">Step 4: Activating Your Local Shield</h2>
<p>Creating the configuration configuration file isn't enough; you must explicitly instruct Git to bind itself to the pipeline manager engine.</p>
<p>Run this command inside your project root:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">pre-commit</span><span class="sfazB"> install</span><span class="sMK4o">```
</span></span><span class="line" line="2"><span emptyLinePlaceholder>
</span></span><span class="line" line="3"><span class="sBMFI">You</span><span class="sfazB"> will see a success output: </span><span class="sMK4o">`</span><span class="sBMFI">pre-commit</span><span class="sfazB"> installed</span><span class="sfazB"> at</span><span class="sfazB"> .git/hooks/pre-commit</span><span class="sMK4o">`</span><span class="s2Zo4">.
</span></span><span class="line" line="4"><span emptyLinePlaceholder>
</span></span><span class="line" line="5"><span class="sBMFI">From</span><span class="sfazB"> now on, whenever you execute a </span><span class="sMK4o">`</span><span class="sBMFI">git</span><span class="sfazB"> commit</span><span class="sMK4o">`</span><span class="sBMFI">,</span><span class="sfazB"> the pre-commit manager intercepts the action, extracts only your staged code changes, and runs them through your configured tools.
</span></span><span class="line" line="6"><span emptyLinePlaceholder>
</span></span><span class="line" line="7"><span class="sHwdD">### What Happens When a Hook Fails?
</span></span><span class="line" line="8"><span class="sBMFI">If</span><span class="sfazB"> the pipeline catches an error (e.g., you left extra spaces or </span><span class="sMK4o">`</span><span class="sBMFI">gitleaks</span><span class="sMK4o">`</span><span class="sBMFI"> flags</span><span class="sfazB"> a secret string), the process will abort entirely:
</span></span><span class="line" line="9"><span emptyLinePlaceholder>
</span></span><span class="line" line="10"><span class="sMK4o">```</span><span class="sBMFI">text
</span></span><span class="line" line="11"><span class="sBMFI">Trailing</span><span class="sfazB"> Whitespace..................................Failed
</span></span><span class="line" line="12"><span class="sBMFI">End</span><span class="sfazB"> of</span><span class="sfazB"> File</span><span class="sfazB"> Fixer....................................Passed
</span></span><span class="line" line="13"><span class="sBMFI">Gitleaks</span><span class="sfazB"> System......................................Passed
</span></span><span class="line" line="14"><span class="sMK4o">[</span><span class="sTEyZ">x</span><span class="sMK4o">]</span><span class="sTEyZ"> Commit blocked</span><span class="sMK4o">!</span><span class="sBMFI"> Fix</span><span class="sfazB"> the</span><span class="sfazB"> formatting</span><span class="sfazB"> errors</span><span class="sfazB"> and</span><span class="sfazB"> stage</span><span class="sfazB"> files</span><span class="sfazB"> again.
</span></span></code></pre>
<p>The tool will often automatically fix the formatting files directly in place for you. All you have to do is re-stage the corrected modifications (<code>git add .</code>) and re-run your commit command!</p>
<h2 id="step-5-forcing-a-complete-manual-review">Step 5: Forcing a Complete Manual Review</h2>
<p>Hooks are configured by default to only scan files that are actively changing in your current commit. If you want to force the engine to audit every single file across your entire legacy workspace tree right now, execute:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">pre-commit</span><span class="sfazB"> run</span><span class="sfazB"> --all-files
</span></span></code></pre>
<p>This is highly recommended when introducing pre-commit pipelines into an older, existing project for the first time to clean out historical style issues.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>By introducing automated pre-commit triggers, you effectively build a lightweight continuous integration layer directly on your workspace terminal. You save yourself from pipeline failure notifications, protect your infrastructure from credential leaks, and guarantee that every commit pushed to your remote repository is structurally clean.</p>
<style>
html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}
</style>]]></content:encoded>
            <author>no-reply@yourdomain.com (Gideon Yebei) (Gideon Yebei)</author>
        </item>
        <item>
            <title><![CDATA[Master Git Housekeeping - Auto-Pruning Dead Tracking Branches and Fixing Detached HEADs]]></title>
            <link>https://nodewave-blogs.vercel.app/blogs/git-housekeeping</link>
            <guid isPermaLink="false">https://nodewave-blogs.vercel.app/blogs/git-housekeeping</guid>
            <pubDate>Sat, 27 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Clean up the debris after a massive local sync. Learn how to automatically drop deleted remote tracking branches and rescue your code from a detached HEAD state.]]></description>
            <content:encoded><![CDATA[<p>Once you automate your local branch syncs, you will quickly notice a secondary annoying problem: <strong>ghost branches</strong>.</p>
<p>Your teammates delete their feature branches on</p>
<p><a href="https://github.com/" rel="[\"nofollow\"]">GitHub</a></p>
<p>after pulling a pull request, but those references still clutter your local <code>git branch -a</code> view forever. Worse yet, if an automated script or manual checkout accidentally lands you on a raw commit identifier instead of a formal pointer, you will drop straight into the twilight zone known as a <strong>detached HEAD state</strong>.</p>
<p>Here is how to automate your repository cleanup and rescue your code when Git loses its coordinates.</p>
<h2 id="step-1-automate-stale-branch-pruning">Step 1: Automate Stale Branch Pruning</h2>
<p>By default, when you run <code>git fetch</code> or <code>git pull</code>, Git keeps your local tracking copies of remote branches even if someone deleted them from the main cloud server. Your local machine is left holding a graveyard of stale references.</p>
<p>You can manually clean them up using:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">git</span><span class="sfazB"> fetch</span><span class="sfazB"> --prune
</span></span></code></pre>
<p>But you shouldn't have to remember to type that flag every single time.</p>
<h3 id="the-permanent-fix-run-this-global-configuration-command-to-instruct-git-to-automatically-sweep-away-dead-remote-branch-pointers-during-every-single-sync-operation">The Permanent Fix Run this global configuration command to instruct Git to automatically sweep away dead remote branch pointers during every single sync operation:</h3>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">git</span><span class="sfazB"> config</span><span class="sfazB"> --global</span><span class="sfazB"> fetch.prune</span><span class="sMK4o"> true
</span></span></code></pre>
<p>Now, your project history list actively auto-cleans itself without manual intervention.</p>
<h2 id="step-2-demystifying-and-fixing-the-detached-head">Step 2: Demystifying and Fixing the "Detached HEAD"</h2>
<p>A detached HEAD state occurs when your terminal pointer (<code>HEAD</code>) is looking directly at a specific <strong>commit hash</strong> rather than a named local branch (like <code>main</code> or <code>feature-login</code>).</p>
<h3 id="how-you-usually-get-there-you-checked-out-a-raw-commit-to-inspect-old-code-git-checkout-7a1b3c2">How you usually get there:- You checked out a raw commit to inspect old code: <code>git checkout 7a1b3c2</code></h3>
<ul>
  <li>You checked out a remote branch directly without creating a local copy: <code>git checkout origin/feature-x</code>- An automation script errored out mid-transition.</li>
</ul>
<h3 id="the-danger-zoneyou-can-still-write-code-modify-files-and-make-commits-while-in-a-detached-head-state-however-these-new-commits-are-not-attached-to-any-branch-if-you-switch-back-to-main-your-new-changes-will-vanish-into-the-background-leaving-them-open-to-being-permanently-deleted-by-gits-garbage-collector">The Danger ZoneYou can still write code, modify files, and make commits while in a detached HEAD state. However, <strong>these new commits are not attached to any branch</strong>. If you switch back to <code>main</code>, your new changes will vanish into the background, leaving them open to being permanently deleted by Git's garbage collector.</h3>
<h2 id="step-3-the-detached-head-rescue-mission">Step 3: The Detached HEAD Rescue Mission</h2>
<p>If you realize you made commits while detached, do not panic. Your work is completely safe if you follow these precise steps.</p>
<h3 id="scenario-a-you-havent-left-the-detached-state-yetif-your-terminal-currently-says-head-detached-at-simply-wrap-your-floating-commits-into-a-brand-new-branch-immediately">Scenario A: You haven't left the detached state yetIf your terminal currently says <code>HEAD detached at...</code>, simply wrap your floating commits into a brand new branch immediately:</h3>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sHwdD"># Create a new branch and switch to it right now
</span></span><span class="line" line="2"><span class="sBMFI">git</span><span class="sfazB"> checkout</span><span class="sfazB"> -b</span><span class="sfazB"> feature-rescued-work
</span></span></code></pre>
<p>Git automatically anchors all your floating commits directly to this new branch name. You can now safely merge it back into your primary workflow.</p>
<h3 id="scenario-b-you-already-switched-branches-and-your-work-vanishedif-you-accidentally-switched-back-to-main-and-your-recent-work-disappeared-gits-commit-history-tool-git-log-wont-show-it-you-must-query-the-deep-internal-log-system">Scenario B: You already switched branches and your work "vanished"If you accidentally switched back to <code>main</code> and your recent work disappeared, Git's commit history tool (<code>git log</code>) won't show it. You must query the deep internal log system:</h3>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">git</span><span class="sfazB"> reflog
</span></span></code></pre>
<p>This outputs a master transaction history of everywhere your terminal pointer has moved:</p>
<pre class="[\"language-text\"]"><code>7a1b3c2 HEAD@{0}: checkout: moving from 9f3e4d5 to main
9f3e4d5 HEAD@{1}: commit: Added critical hotfix code in detached state
7a1b3c2 HEAD@{2}: checkout: moving to 7a1b3c2
</code></pre>
<ol>
  <li>Find the commit hash where you wrote your work (in this case, <code>9f3e4d5</code>).2. Target that hash to build a formal rescue branch:</li>
</ol>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">git</span><span class="sfazB"> branch</span><span class="sfazB"> feature-recovered-commits</span><span class="sfazB"> 9f3e4d5
</span></span></code></pre>
<p>Your changes are successfully pulled from the void into a clean, permanent tracking pointer.</p>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>Maintaining a pristine repository requires a blend of automated configurations and conceptual awareness. By enforcing automatic reference pruning and mastering the mechanics of the <code>reflog</code>, you ensure your developer workspace remains highly performant, predictable, and resilient against unexpected operational mistakes.</p>
<style>
html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}
</style>]]></content:encoded>
            <author>no-reply@yourdomain.com (Gideon Yebei) (Gideon Yebei)</author>
        </item>
        <item>
            <title><![CDATA[Debug Pipelines Locally - The Complete Guide to Running GitHub Actions with Act]]></title>
            <link>https://nodewave-blogs.vercel.app/blogs/local-github-actions</link>
            <guid isPermaLink="false">https://nodewave-blogs.vercel.app/blogs/local-github-actions</guid>
            <pubDate>Fri, 26 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Learn how to use Act to run, test, and step-debug your GitHub Actions workflows locally, saving time and keeping your commit history clean.]]></description>
            <content:encoded><![CDATA[<p>We have all been trapped in the infamous <strong>CI/CD commit loop</strong>. You make a change to a <code>.github/workflows/ci.yml</code> file, commit it, push it, wait five minutes for a cloud runner to spin up, and realize you missed a minor indentation or a basic syntax flag.</p>
<p>Ten commits later, your Git history is littered with messages like <code>"fix ci"</code>, <code>"fix ci again"</code>, and <code>"please work"</code>.</p>
<p>It doesn’t have to be this way. By utilizing <strong>Act</strong>, an open-source tool that reads your GitHub Actions files and spins them up inside local Docker containers, you can run, test, and debug your entire automation pipeline locally in seconds.</p>
<hr />
<h2 id="step-1-core-prerequisites-and-architecture">Step 1: Core Prerequisites and Architecture</h2>
<p>Under the hood, <code>act</code> uses your local container engine to mimic the hosted virtual environments provided by GitHub (like <code>ubuntu-latest</code>).</p>
<p>Before starting, ensure you have the following installed and running:</p>
<ol>
  <li><strong>Docker Desktop</strong> (or Podman / Docker Engine via WSL2).</li>
  <li><strong>Your Terminal Shell</strong> (Bash, Zsh, or PowerShell).</li>
</ol>
<h3 id="installing-act">Installing Act</h3>
<p>Install the binary framework via your system package manager:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sHwdD"># macOS (Homebrew)
</span></span><span class="line" line="2"><span class="sBMFI">brew</span><span class="sfazB"> install</span><span class="sfazB"> nektos/tap/act
</span></span><span class="line" line="3"><span emptyLinePlaceholder>
</span></span><span class="line" line="4"><span class="sHwdD"># Windows (Winget or Scoop)
</span></span><span class="line" line="5"><span class="sBMFI">winget</span><span class="sfazB"> install</span><span class="sfazB"> nektos.act
</span></span><span class="line" line="6"><span class="sHwdD"># OR: scoop install act
</span></span><span class="line" line="7"><span emptyLinePlaceholder>
</span></span><span class="line" line="8"><span class="sHwdD"># Linux (Bash script installation)
</span></span><span class="line" line="9"><span class="sBMFI">curl</span><span class="sfazB"> --proto</span><span class="sMK4o"> '</span><span class="sfazB">=https</span><span class="sMK4o">'</span><span class="sfazB"> --tlsv1.2</span><span class="sfazB"> -sSf</span><span class="sfazB"> https://githubusercontent.com</span><span class="sMK4o"> |</span><span class="sBMFI"> sudo</span><span class="sfazB"> sh
</span></span></code></pre>
<p>Verify your installation works by checking the core version output:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">act</span><span class="sfazB"> --version
</span></span></code></pre>
<hr />
<h2 id="step-2-the-initial-sandbox-setup">Step 2: The Initial Sandbox Setup</h2>
<p>The first time you execute <code>act</code> inside a repository, it will ask you to select a default Docker image size to represent the <code>ubuntu-latest</code> runner.</p>
<p>Run a basic list command to trigger this configuration menu:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">act</span><span class="sfazB"> -l
</span></span></code></pre>
<h3 id="choosing-the-right-runner-size">Choosing the Right Runner Size:</h3>
<ul>
  <li><strong>Micro (Default):</strong> ~200MB image. Fast to download but contains almost no tools. You will manually have to install basics like <code>curl</code>, <code>git</code>, or languages inside your steps.</li>
  <li><strong>Medium:</strong> ~500MB+ image. Contains common build tools, Node.js, Python, and Docker dependencies. <strong>(Highly Recommended for most devs)</strong>.</li>
  <li><strong>Large:</strong> ~18GB+ image. A near-exact match of the actual GitHub cloud environment. Massive download, but highly accurate.</li>
</ul>
<p><em>Note: If you want to change your mind later, you can modify these choices inside your global config file at <code>~/.actrc</code>.</em></p>
<hr />
<h2 id="step-3-executing-your-local-pipeline">Step 3: Executing Your Local Pipeline</h2>
<p>Imagine you have a standard test workflow saved inside <code>.github/workflows/test.yml</code> that triggers on a code push event:</p>
<pre class="language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="swJcz">name</span><span class="sMK4o">:</span><span class="sfazB"> Core Test Suite
</span></span><span class="line" line="2"><span class="sfNiH">on</span><span class="sMK4o">:</span><span class="sMK4o"> [</span><span class="sfazB">push</span><span class="sMK4o">]
</span></span><span class="line" line="3"><span emptyLinePlaceholder>
</span></span><span class="line" line="4"><span class="swJcz">jobs</span><span class="sMK4o">:
</span></span><span class="line" line="5"><span class="swJcz">  build-and-test</span><span class="sMK4o">:
</span></span><span class="line" line="6"><span class="swJcz">    runs-on</span><span class="sMK4o">:</span><span class="sfazB"> ubuntu-latest
</span></span><span class="line" line="7"><span class="swJcz">    steps</span><span class="sMK4o">:
</span></span><span class="line" line="8"><span class="sMK4o">      -</span><span class="swJcz"> name</span><span class="sMK4o">:</span><span class="sfazB"> Checkout Code
</span></span><span class="line" line="9"><span class="swJcz">        uses</span><span class="sMK4o">:</span><span class="sfazB"> actions/checkout@v4
</span></span><span class="line" line="10"><span emptyLinePlaceholder>
</span></span><span class="line" line="11"><span class="sMK4o">      -</span><span class="swJcz"> name</span><span class="sMK4o">:</span><span class="sfazB"> Setup Node Environment
</span></span><span class="line" line="12"><span class="swJcz">        uses</span><span class="sMK4o">:</span><span class="sfazB"> actions/setup-node@v4
</span></span><span class="line" line="13"><span class="swJcz">        with</span><span class="sMK4o">:
</span></span><span class="line" line="14"><span class="swJcz">          node-version</span><span class="sMK4o">:</span><span class="sbssI"> 20
</span></span><span class="line" line="15"><span emptyLinePlaceholder>
</span></span><span class="line" line="16"><span class="sMK4o">      -</span><span class="swJcz"> name</span><span class="sMK4o">:</span><span class="sfazB"> Install and Run Tests
</span></span><span class="line" line="17"><span class="swJcz">        run</span><span class="sMK4o">:</span><span class="s7zQu"> |
</span></span><span class="line" line="18"><span class="sfazB">          npm ci
</span></span><span class="line" line="19"><span class="sfazB">          npm test
</span></span></code></pre>
<p>To run this workflow completely offline from the root of your project directory, simply run:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">act</span><span class="sfazB"> push
</span></span></code></pre>
<p><code>act</code> will intercept the <code>push</code> event string, match it to the workflow file, compile your code directories directly into a Docker container workspace volumes matrix, and stream the console outputs in real-time.</p>
<hr />
<h2 id="step-4-advanced-scenarios-events-secrets-and-artifacts">Step 4: Advanced Scenarios — Events, Secrets, and Artifacts</h2>
<p>Real pipelines are rarely as simple as a basic push. They rely on webhooks, secure deployment keys, and matrix configurations. Here is how to manage complex environments locally.</p>
<h3 id="simulating-specific-webhook-events">Simulating Specific Webhook Events</h3>
<p>If your workflow triggers on a complex event like a <code>pull_request</code> or an <code>issue_comment</code>, you can pass mock JSON payloads to test targeted behaviors.</p>
<p>Create a file named <code>mock-event.json</code>:</p>
<pre class="language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight"pull_request": {
    "head": { "ref": "feature-branch" },
    "base": { "ref": "main" }
  }
}
" style=""><code><span class="line" line="1"><span class="sMK4o">{
</span></span><span class="line" line="2"><span class="sMK4o">  "</span><span class="spNyl">pull_request</span><span class="sMK4o">"</span><span class="sMK4o">:</span><span class="sMK4o"> {
</span></span><span class="line" line="3"><span class="sMK4o">    "</span><span class="sBMFI">head</span><span class="sMK4o">"</span><span class="sMK4o">:</span><span class="sMK4o"> {</span><span class="sMK4o"> "</span><span class="sbssI">ref</span><span class="sMK4o">"</span><span class="sMK4o">:</span><span class="sMK4o"> "</span><span class="sfazB">feature-branch</span><span class="sMK4o">"</span><span class="sMK4o"> },
</span></span><span class="line" line="4"><span class="sMK4o">    "</span><span class="sBMFI">base</span><span class="sMK4o">"</span><span class="sMK4o">:</span><span class="sMK4o"> {</span><span class="sMK4o"> "</span><span class="sbssI">ref</span><span class="sMK4o">"</span><span class="sMK4o">:</span><span class="sMK4o"> "</span><span class="sfazB">main</span><span class="sMK4o">"</span><span class="sMK4o"> }
</span></span><span class="line" line="5"><span class="sMK4o">  }
</span></span><span class="line" line="6"><span class="sMK4o">}
</span></span></code></pre>
<p>Trigger your workflow by passing the event type and payload path flags:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">act</span><span class="sfazB"> pull_request</span><span class="sfazB"> -e</span><span class="sfazB"> mock-event.json
</span></span></code></pre>
<h3 id="safely-injecting-secrets-and-variables">Safely Injecting Secrets and Variables</h3>
<p>Never hardcode production API tokens or environment configs into your files. <code>act</code> allows you to load mock credentials locally using <code>.env</code> syntax files.</p>
<p>Create a file named <code>.secrets</code> (and add it to your <code>.gitignore</code> immediately!):</p>
<pre class="language-env shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span>AWS_ACCESS_KEY_ID=MOCK_LOCAL_KEY_12345
</span></span><span class="line" line="2"><span>DEPLOY_TOKEN=ghp_MockSecretTokenStringHere
</span></span></code></pre>
<p>Inject these variables directly into your execution context using the secure secret loader flag:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">act</span><span class="sfazB"> --secret-file</span><span class="sfazB"> .secrets
</span></span></code></pre>
<h3 id="isolating-individual-jobs">Isolating Individual Jobs</h3>
<p>If your workflow file contains multiple long-running jobs (e.g., <code>lint</code>, <code>test</code>, <code>deploy</code>) and you only want to focus on fixing a specific piece, use the targeted job parameter flag:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">act</span><span class="sfazB"> -j</span><span class="sfazB"> build-and-test
</span></span></code></pre>
<hr />
<h2 id="step-5-common-gotchas-and-limitations">Step 5: Common Gotchas and Limitations</h2>
<p>While <code>act</code> is incredibly powerful, it is a simulation wrapper, not an identical duplicate of the GitHub cloud architecture. Keep these structural limitations in mind:</p>
<ul>
  <li><strong>Windows/macOS Runners:</strong> <code>act</code> runs natively inside Linux Docker environments. If your workflow declares <code>runs-on: windows-latest</code> or <code>runs-on: macos-latest</code>, <code>act</code> will attempt to run them inside Linux wrappers, which will fail if you rely on native binaries like MSBuild or Xcode.</li>
  <li><strong>The Checkout Action Caveat:</strong> The native <code>actions/checkout@v4</code> action usually fetches historical branches. When executed via <code>act</code>, it copies your live local tracking directory as-is. Make sure your local folder doesn't contain giant compiled file structures that will slow down your container mounting states! Use a <code>.actignore</code> file to omit massive binary data.</li>
</ul>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>Adding <code>act</code> to your localized DevOps toolchain fundamentally changes how you develop automation infrastructure. It shortens your loop testing times from minutes to fractions of a second, protects your repository's commit history from pollution, and ensures that when your pipeline finally makes it to the cloud, it works flawlessly on the very first try.</p>
<style>
html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}
</style>]]></content:encoded>
            <author>no-reply@yourdomain.com (Gideon Yebei) (Gideon Yebei)</author>
        </item>
        <item>
            <title><![CDATA[Safely Update All Local Git Branches At Once Without Breaking Your Working Tree]]></title>
            <link>https://nodewave-blogs.vercel.app/blogs/git-pull-all</link>
            <guid isPermaLink="false">https://nodewave-blogs.vercel.app/blogs/git-pull-all</guid>
            <pubDate>Fri, 26 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Stop manual checkouts. Learn why your naive git pull loops fail and discover the correct, automation-friendly way to fast-forward all local tracking branches at once.]]></description>
            <content:encoded><![CDATA[<p>Picture this scenario. You return to a massive project repository after a long weekend. Dozens of team branches have updated on GitHub, and your local workspace feels incredibly left behind.</p>
<p>Naturally, you decide to write a quick, clever shell script line to pull everything down at once:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="s7zQu">for</span><span class="sTEyZ"> branch </span><span class="s7zQu">in</span><span class="sTEyZ"> \(</span><span class="sMK4o">(</span><span class="sBMFI">git</span><span class="sfazB"> branch</span><span class="sfazB"> -r</span><span class="sTEyZ"> \v</span><span class="sfazB">ert{}</span><span class="sfazB"> grep</span><span class="sfazB"> -v</span><span class="sMK4o"> '</span><span class="sfazB">\-&gt;</span><span class="sMK4o">'</span><span class="sMK4o">);</span><span class="s7zQu"> do</span><span class="sBMFI"> git</span><span class="sfazB"> pull</span><span class="sTEyZ">\)</span><span class="sfazB">branch</span><span class="sMK4o">;</span><span class="s7zQu"> done
</span></span></code></pre>
<p>You hit Enter, expecting a beautifully synced repository. Instead, your console explodes into a mess of syntax errors, detached HEAD states, and unexpected merge conflicts.</p>
<p>Here is why your native <code>git pull</code> loops are broken, how to fix them, and the ultimate safe alternative.</p>
<h2 id="why-the-naive-script-breaks-your-repo">Why The Naive Script Breaks Your Repo</h2>
<p>To understand why the loop above fails, you have to look closely at how <code>git pull</code> operates under the hood. The command relies on two distinct expectations:</p>
<ol>
  <li><strong>Active Checkouts Only:</strong> <code>git pull</code> is explicitly designed to download remote changes and immediately merge them into your <em>currently checked-out</em> local branch. It is physically incapable of updating separate background branches.</li>
  <li><strong>Argument Syntax:</strong> <code>git branch -r</code> outputs remote tracking references prefixed with the remote identifier (e.g., <code>origin/feature-auth</code>). Passing <code>origin/feature-auth</code> directly to a <code>git pull</code> command breaks structural syntax.</li>
</ol>
<p>If you run this inside your <code>main</code> branch, the loop will continuously try to force-merge <em>every single remote branch</em> into your clean <code>main</code> history.</p>
<h2 id="the-instant-fix-trust-git-fetch-all">The Instant Fix: Trust <code>git fetch --all</code></h2>
<p>Before writing custom automation scripts, remember that you rarely need to explicitly run a <code>git pull</code> on branches you aren't currently coding on.</p>
<p>If you simply want to download the latest state of the entire project to inspect histories or check things out later, run this native command:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">git</span><span class="sfazB"> fetch</span><span class="sfazB"> --all
</span></span></code></pre>
<h3 id="why-this-is-your-best-default">Why this is your best default:</h3>
<ul>
  <li><strong>Zero Risk:</strong> It downloads the raw remote objects and updates remote tracking pointers (like <code>origin/main</code>) without modifying your current workspace files.</li>
  <li><strong>No Merge Conflicts:</strong> It will never accidentally force a broken merge or pollute your active staging index.</li>
  <li><strong>Server Friendly:</strong> It makes a single optimized network request instead of hitting your server individually for every branch reference.</li>
</ul>
<hr />
<h2 id="building-the-automated-local-multi-pull-script">Building the Automated Local Multi-Pull Script</h2>
<p>If you truly need every single <strong>local</strong> branch to immediately step forward and match its remote counterpart, you must explicitly force Git to safely navigate between branches.</p>
<p>Open a new shell script or your <code>.bashrc</code>/<code>.zshrc</code> file and add this robust layout:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight"\$has_changes" ]; then
    echo "Saving uncommitted work to stash..."
    git stash -u
fi

# 2. Save your current branch name to return safely later
current_branch=\$(git rev-parse --abbrev-ref HEAD)

# 3. Safely loop through clean local branch lists
for branch in \$(git for-each-ref --format='%(refname:short)' refs/heads/); do
    echo "Checking out \$branch..."
    git checkout "\$branch" 2>/dev/null

    # Use fast-forward only to avoid generating messy, unnecessary merge commits
    echo "Applying remote fast-forward updates..."
    git pull --ff-only
done

# 4. Return to your starting point and restore work
git checkout "\$current_branch" 2>/dev/null
if [ -n "\$has_changes" ]; then
    echo "Restoring your original uncommitted changes..."
    git stash pop
fi

echo "✨ All local branches synced successfully!"
" style=""><code><span class="line" line="1"><span class="sHwdD">#!/bin/bash
</span></span><span class="line" line="2"><span emptyLinePlaceholder>
</span></span><span class="line" line="3"><span class="sHwdD"># 1. Stash any uncommitted workspace changes so they aren't lost
</span></span><span class="line" line="4"><span class="sTEyZ">has_changes</span><span class="sMK4o">=</span><span class="sTEyZ">\$</span><span class="sMK4o">(</span><span class="sBMFI">git</span><span class="sfazB"> status</span><span class="sfazB"> --porcelain</span><span class="sMK4o">)
</span></span><span class="line" line="5"><span class="s7zQu">if</span><span class="sMK4o"> [</span><span class="sMK4o"> -n</span><span class="sMK4o"> "</span><span class="sTEyZ">\$</span><span class="sfazB">has_changes</span><span class="sMK4o">"</span><span class="sMK4o"> ];</span><span class="s7zQu"> then
</span></span><span class="line" line="6"><span class="s2Zo4">    echo</span><span class="sMK4o"> "</span><span class="sfazB">Saving uncommitted work to stash...</span><span class="sMK4o">"
</span></span><span class="line" line="7"><span class="sBMFI">    git</span><span class="sfazB"> stash</span><span class="sfazB"> -u
</span></span><span class="line" line="8"><span class="s7zQu">fi
</span></span><span class="line" line="9"><span emptyLinePlaceholder>
</span></span><span class="line" line="10"><span class="sHwdD"># 2. Save your current branch name to return safely later
</span></span><span class="line" line="11"><span class="sTEyZ">current_branch</span><span class="sMK4o">=</span><span class="sTEyZ">\$</span><span class="sMK4o">(</span><span class="sBMFI">git</span><span class="sfazB"> rev-parse</span><span class="sfazB"> --abbrev-ref</span><span class="sfazB"> HEAD</span><span class="sMK4o">)
</span></span><span class="line" line="12"><span emptyLinePlaceholder>
</span></span><span class="line" line="13"><span class="sHwdD"># 3. Safely loop through clean local branch lists
</span></span><span class="line" line="14"><span class="s7zQu">for</span><span class="sTEyZ"> branch </span><span class="s7zQu">in</span><span class="sTEyZ"> \$</span><span class="sMK4o">(</span><span class="sBMFI">git</span><span class="sfazB"> for-each-ref</span><span class="sfazB"> --format=</span><span class="sMK4o">'</span><span class="sfazB">%(refname:short)</span><span class="sMK4o">'</span><span class="sfazB"> refs/heads/</span><span class="sMK4o">);</span><span class="s7zQu"> do
</span></span><span class="line" line="15"><span class="s2Zo4">    echo</span><span class="sMK4o"> "</span><span class="sfazB">Checking out </span><span class="sTEyZ">\$</span><span class="sfazB">branch...</span><span class="sMK4o">"
</span></span><span class="line" line="16"><span class="sBMFI">    git</span><span class="sfazB"> checkout</span><span class="sMK4o"> "</span><span class="sTEyZ">\$</span><span class="sfazB">branch</span><span class="sMK4o">"</span><span class="sMK4o"> 2&gt;</span><span class="sfazB">/dev/null
</span></span><span class="line" line="17"><span emptyLinePlaceholder>
</span></span><span class="line" line="18"><span class="sHwdD">    # Use fast-forward only to avoid generating messy, unnecessary merge commits
</span></span><span class="line" line="19"><span class="s2Zo4">    echo</span><span class="sMK4o"> "</span><span class="sfazB">Applying remote fast-forward updates...</span><span class="sMK4o">"
</span></span><span class="line" line="20"><span class="sBMFI">    git</span><span class="sfazB"> pull</span><span class="sfazB"> --ff-only
</span></span><span class="line" line="21"><span class="s7zQu">done
</span></span><span class="line" line="22"><span emptyLinePlaceholder>
</span></span><span class="line" line="23"><span class="sHwdD"># 4. Return to your starting point and restore work
</span></span><span class="line" line="24"><span class="sBMFI">git</span><span class="sfazB"> checkout</span><span class="sMK4o"> "</span><span class="sTEyZ">\$</span><span class="sfazB">current_branch</span><span class="sMK4o">"</span><span class="sMK4o"> 2&gt;</span><span class="sfazB">/dev/null
</span></span><span class="line" line="25"><span class="s7zQu">if</span><span class="sMK4o"> [</span><span class="sMK4o"> -n</span><span class="sMK4o"> "</span><span class="sTEyZ">\$</span><span class="sfazB">has_changes</span><span class="sMK4o">"</span><span class="sMK4o"> ];</span><span class="s7zQu"> then
</span></span><span class="line" line="26"><span class="s2Zo4">    echo</span><span class="sMK4o"> "</span><span class="sfazB">Restoring your original uncommitted changes...</span><span class="sMK4o">"
</span></span><span class="line" line="27"><span class="sBMFI">    git</span><span class="sfazB"> stash</span><span class="sfazB"> pop
</span></span><span class="line" line="28"><span class="s7zQu">fi
</span></span><span class="line" line="29"><span emptyLinePlaceholder>
</span></span><span class="line" line="30"><span class="s2Zo4">echo</span><span class="sMK4o"> "</span><span class="sfazB">✨ All local branches synced successfully!</span><span class="sMK4o">"
</span></span></code></pre>
<h3 id="key-upgrades-in-this-script">Key upgrades in this script:</h3>
<ul>
  <li><strong>Stash Safety Net:</strong> It actively checks for uncommitted edits and temporarily shelves them so your checkout transitions don't error out.</li>
  <li><strong>Fast-Forward Restrictions:</strong> Using <code>--ff-only</code> ensures that if a local branch has split or drifted too far from the remote history, the script will gracefully skip it rather than creating an automated merge mess.</li>
</ul>
<hr />
<h2 id="convert-this-into-a-permanent-git-alias">Convert This Into a Permanent Git Alias</h2>
<p>Typing out long Bash scripts every time you open a project gets exhausting. Let's register this tool directly into your core Git configuration so it is accessible globally.</p>
<p>Open your global git configuration file:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">git</span><span class="sfazB"> config</span><span class="sfazB"> --global</span><span class="sfazB"> --edit
</span></span></code></pre>
<p>Find the <code>[alias]</code> block and add a new custom command named <code>pull-all</code>:</p>
<pre class="language-ini shiki shiki-themes material-theme-lighter material-theme material-theme-palenight"!f() { \
        curr=\$(git rev-parse --abbrev-ref HEAD); \
        for b in \$(git for-each-ref --format='%(refname:short)' refs/heads/); do \
            git checkout \$b && git pull --ff-only; \
        done; \
        git checkout \$curr; \
    }; f"
" style=""><code><span class="line" line="1"><span>[alias]
</span></span><span class="line" line="2"><span>    pull-all = "!f() { \
</span></span><span class="line" line="3"><span>        curr=\$(git rev-parse --abbrev-ref HEAD); \
</span></span><span class="line" line="4"><span>        for b in \$(git for-each-ref --format='%(refname:short)' refs/heads/); do \
</span></span><span class="line" line="5"><span>            git checkout \$b && git pull --ff-only; \
</span></span><span class="line" line="6"><span>        done; \
</span></span><span class="line" line="7"><span>        git checkout \$curr; \
</span></span><span class="line" line="8"><span>    }; f"
</span></span></code></pre>
<p>Save and exit. Now, clean updates across your entire workspace are compressed into a single elegant command:</p>
<pre class="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span class="sBMFI">git</span><span class="sfazB"> pull-all
</span></span></code></pre>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>Automating your terminal flow should never come at the expense of your repository's stability. By switching from a raw <code>git pull</code> loop to an intentional, state-aware layout or leveraging <code>git fetch --all</code>, you keep your workflows smooth, fast, and entirely conflict-free.</p>
<icon name="i-simple-icons-nuxtdotjs"></icon>
<note>
  <p>Here is some additional information for your reader.</p>
</note>
<tip>
  <p>Here is a helpful suggestion.</p>
</tip>
<warning>
  <p>Be careful with this action as it might have unexpected results.</p>
</warning>
<caution>
  <p>This action cannot be undone.</p>
</caution>
<tabs>
  <tabs-item icon="i-lucide-code" label="Code">
    <callout>
      <p>Lorem velit voluptate ex reprehenderit ullamco et culpa.</p>
    </callout>
    <pre class="[\"language-text\"]"Preview" icon="i-lucide-eye"}

    ::callout
    Lorem velit voluptate ex reprehenderit ullamco et culpa.
    ::

    :::

    ::
    "><code>
    :::

    :::tabs-item{label="Preview" icon="i-lucide-eye"}

    ::callout
    Lorem velit voluptate ex reprehenderit ullamco et culpa.
    ::

    :::

    ::
    </code></pre>
    <iframe src="https://www.youtube-nocookie.com/embed/_eQxomah-nA?si=pDSzchUBDKb2NQu7" title="YouTube video player" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen style="aspect-ratio: 16/9; width: 100%;"></iframe>
    <iframe style="border: 1px solid rgba(0, 0, 0, 0.1); width: 100%; height: 450px;" src="https://embed.figma.com/file/1544369209862884086/hf_embed?community_viewer=true&embed_host=fastma&fuid=960610330589944894&kind=file&page-selector=0&viewer=1" allowFullScreen></iframe>
  </tabs-item>
</tabs>
<style>
html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}
</style>]]></content:encoded>
            <author>no-reply@yourdomain.com (Gideon Yebei) (Gideon Yebei)</author>
        </item>
        <item>
            <title><![CDATA[Bash-ify Your PowerShell - The Ultimate Guide to Autosuggestions, Completions, and Subcommands]]></title>
            <link>https://nodewave-blogs.vercel.app/blogs/powershell-bash</link>
            <guid isPermaLink="false">https://nodewave-blogs.vercel.app/blogs/powershell-bash</guid>
            <pubDate>Tue, 16 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Learn how to make PowerShell look, feel, and behave exactly like a premium Bash/Zsh environment with autosuggestions, interactive subcommand completions, and fuzzy history search.]]></description>
            <content:encoded><![CDATA[<p>Learn how to make PowerShell look, feel, and behave exactly like a premium Bash/Zsh environment with autosuggestions, interactive subcommand completions, and fuzzy history search.</p>
<p>Let’s be honest. If you are coming from a Linux or macOS background, opening up a raw PowerShell (<code>pwsh</code>) terminal can feel a bit like stepping into a parallel universe where everything is <em>almost</em> familiar, but just clunky enough to drive you crazy.</p>
<p>You miss the instant, predictive text of <code>fish</code>, the robust history search of <code>zsh</code>, and the effortless subcommand completions of a well-tuned <code>bash</code> setup. Out of the box, PowerShell’s tab-completion feels slow, and its visual feedback is... lacking.</p>
<p>But here is the secret: <strong>PowerShell is secretly an absolute powerhouse of customizability.</strong> With a few modern modules and a slick profile configuration, you can make <code>pwsh</code> look, feel, and behave exactly like a premium Bash/Zsh environment—all while retaining PowerShell's insane object-oriented pipeline capabilities.</p>
<p>Here is how to build the ultimate Bash-ified PowerShell experience.</p>
<h2 id="step-1-fix-the-keybindings-and-enable-fish-style-autosuggestions">Step 1: Fix the Keybindings and Enable Fish-Style Autosuggestions</h2>
<p>The foundation of a good Bash experience is how the line editor behaves. PowerShell handles this via a built-in module called <strong>PSReadLine</strong>. We just need to wake it up and give it the right instructions.</p>
<p>First, open your PowerShell profile in your favorite editor (e.g., VS Code):</p>
<pre class="language-powershell shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span>code $PROFILE
</span></span></code></pre>
<p><em>(If the file doesn't exist yet, run <code>New-Item -Path $PROFILE -Type File -Force</code></em> <em>first).</em></p>
<p>Now, paste these essential configurations into your profile:</p>
<pre class="language-powershell shiki shiki-themes material-theme-lighter material-theme material-theme-palenight"ListView" or "InlineView" (Arrow keys to navigate)
Set-PSReadLineOption -PredictionViewStyle InlineView

# Set the color of the predictive text to be a subtle gray
Set-PSReadLineOption -Colors @{ InlinePrediction = '#666666' }

# Bind the Right Arrow key to accept the current inline suggestion
Set-PSReadLineKeyHandler -Chord 'RightArrow' -Function AcceptSuggestion

# Bind Ctrl+Space to trigger standard menu completion (Zsh style)
Set-PSReadLineKeyHandler -Chord 'Ctrl+Space' -Function MenuComplete

" style=""><code><span class="line" line="1"><span># Set Bash-like Emacs keybindings (Ctrl+A to start of line, Ctrl+E to end, etc.)
</span></span><span class="line" line="2"><span>Set-PSReadLineOption -EditMode Emacs
</span></span><span class="line" line="3"><span emptyLinePlaceholder>
</span></span><span class="line" line="4"><span># Enable Fish-like inline predictive text history
</span></span><span class="line" line="5"><span>Set-PSReadLineOption -PredictionSource History
</span></span><span class="line" line="6"><span emptyLinePlaceholder>
</span></span><span class="line" line="7"><span># Change the prediction view to "ListView" or "InlineView" (Arrow keys to navigate)
</span></span><span class="line" line="8"><span>Set-PSReadLineOption -PredictionViewStyle InlineView
</span></span><span class="line" line="9"><span emptyLinePlaceholder>
</span></span><span class="line" line="10"><span># Set the color of the predictive text to be a subtle gray
</span></span><span class="line" line="11"><span>Set-PSReadLineOption -Colors @{ InlinePrediction = '#666666' }
</span></span><span class="line" line="12"><span emptyLinePlaceholder>
</span></span><span class="line" line="13"><span># Bind the Right Arrow key to accept the current inline suggestion
</span></span><span class="line" line="14"><span>Set-PSReadLineKeyHandler -Chord 'RightArrow' -Function AcceptSuggestion
</span></span><span class="line" line="15"><span emptyLinePlaceholder>
</span></span><span class="line" line="16"><span># Bind Ctrl+Space to trigger standard menu completion (Zsh style)
</span></span><span class="line" line="17"><span>Set-PSReadLineKeyHandler -Chord 'Ctrl+Space' -Function MenuComplete
</span></span></code></pre>
<h3 id="what-this-gives-you">What this gives you:</h3>
<ul>
  <li><strong>Inline History Predictions:</strong> As you type, <code>pwsh</code> will look through your command history and visually suggest the rest of the command in gray text. Press the <strong>Right Arrow</strong> to accept it.</li>
  <li><strong>Zsh-style Menus:</strong> Hit <code>Ctrl + Space</code> to open an interactive grid menu of all available completions right in your terminal buffer.</li>
</ul>
<h2 id="step-2-supercharge-subcommand-completions-with-carapace">Step 2: Supercharge Subcommand Completions with Carapace</h2>
<p>Standard PowerShell only knows how to tab-complete standard cmdlet arguments (like <code>-Path</code> or <code>-Credential</code>). If you type <code>git che[TAB]</code>, it stares at you blankly.</p>
<p>To get full subcommand completion for hundreds of CLI tools (<code>git</code>, <code>docker</code>, <code>kubectl</code>, <code>npm</code>, <code>gh</code>, <code>gcloud</code>), we are going to use <strong>Carapace-bin</strong>, the absolute gold standard for multi-shell command completion.</p>
<ol>
  <li>Install Carapace using a package manager like Scoop or Winget:</li>
</ol>
<pre class="language-powershell shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span>winget install rsteube.carapace
</span></span></code></pre>
<ol start="2">
  <li>Add the initialization logic to your <code>$PROFILE</code>:</li>
</ol>
<pre class="language-powershell shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span># Initialize Carapace completions for PowerShell
</span></span><span class="line" line="2"><span># This hooks into the native engine to provide rich subcommand completions
</span></span><span class="line" line="3"><span>if (Get-Command carapace -ErrorAction SilentlyContinue) {
</span></span><span class="line" line="4"><span>    $old_executioncontext = $ExecutionContext
</span></span><span class="line" line="5"><span>    carapace _powershell | Out-String | Invoke-Expression
</span></span><span class="line" line="6"><span>}
</span></span></code></pre>
<p>Now, try typing <code>git</code> followed by <code>Ctrl + Space</code>. You will see an interactive menu showing options like <code>checkout</code>, <code>commit</code>, <code>clone</code>, complete with help descriptions for every single subcommand!</p>
<h2 id="step-3-add-fuzzy-history-searching-fzf">Step 3: Add Fuzzy History Searching (FZF)</h2>
<p>If you've ever used <code>Ctrl + R</code> in Bash to reverse-search your history using a fuzzy finder, you know you can't live without it. We can bring this exact feature to PowerShell using the <code>PSFzf</code> module.</p>
<ol>
  <li>Install the <code>fzf</code> binary and the PowerShell wrapper module:</li>
</ol>
<pre class="language-powershell shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span>winget install junegunn.fzf
</span></span><span class="line" line="2"><span>Install-Module PSFzf -Scope CurrentUser -Force
</span></span></code></pre>
<ol start="2">
  <li>Add this to your <code>$PROFILE</code>:</li>
</ol>
<pre class="language-powershell shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span>Import-Module PSFzf
</span></span><span class="line" line="2"><span># Overrides Ctrl+R to use the fuzzy finder for your PowerShell history
</span></span><span class="line" line="3"><span>Set-PSReadLineKeyHandler -Chord 'Ctrl+r' -ScriptBlock { [PSFzf]::PostContextHistoryFzf() }
</span></span></code></pre>
<h2 id="step-4-inject-essential-bash-aliases-functions">Step 4: Inject Essential Bash Aliases & Functions</h2>
<p>PowerShell has a few built-in aliases like <code>ls</code> and <code>rm</code>, but they map directly to PowerShell cmdlets (<code>Get-ChildItem</code>, <code>Remove-Item</code>), which don't accept standard Linux flags like <code>-la</code> or <code>-rf</code>.</p>
<p>Let's clean that up by introducing native functions that mirror standard Unix behavior:</p>
<pre class="language-powershell shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" style=""><code><span class="line" line="1"><span># Quick directory listings that accept traditional arguments
</span></span><span class="line" line="2"><span>function ll { Get-ChildItem -Path . -Force | Out-Host }
</span></span><span class="line" line="3"><span>function la { Get-ChildItem -Path . -Force -Recurse | Out-Host }
</span></span><span class="line" line="4"><span emptyLinePlaceholder>
</span></span><span class="line" line="5"><span># A quick wrapper to make 'grep' behave beautifully
</span></span><span class="line" line="6"><span>function grep ($pattern) { Select-String -Pattern $pattern }
</span></span><span class="line" line="7"><span emptyLinePlaceholder>
</span></span><span class="line" line="8"><span># Ensure sudo behavior exists via 'gsudo' (install via winget install gerardog.gsudo)
</span></span><span class="line" line="9"><span>if (Get-Command gsudo -ErrorAction SilentlyContinue) {
</span></span><span class="line" line="10"><span>    Set-Alias -Name sudo -Value gsudo
</span></span><span class="line" line="11"><span>}
</span></span><span class="line" line="12"><span emptyLinePlaceholder>
</span></span><span class="line" line="13"><span># Quick navigation shortcuts
</span></span><span class="line" line="14"><span>function .. { Set-Location .. }
</span></span><span class="line" line="15"><span>function ... { Set-Location ..\.. }
</span></span></code></pre>
<h2 id="step-5-put-it-all-together-your-master-profile">Step 5: Put It All Together (Your Master Profile)</h2>
<p>Here is your complete, optimized <code>$PROFILE</code>. Copy this layout, save the file, and restart your terminal to activate your brand new, incredibly fast Bash-ified environment:</p>
<pre class="language-powershell shiki shiki-themes material-theme-lighter material-theme material-theme-palenight"🚀 PowerShell environment loaded with Bash-UX engine." -ForegroundColor Cyan

" style=""><code><span class="line" line="1"><span># ==========================================
</span></span><span class="line" line="2"><span># 1. PSREADLINE CONFIG (Autosuggestions & Keybindings)
</span></span><span class="line" line="3"><span># ==========================================
</span></span><span class="line" line="4"><span>Import-Module PSReadLine
</span></span><span class="line" line="5"><span>Set-PSReadLineOption -EditMode Emacs
</span></span><span class="line" line="6"><span>Set-PSReadLineOption -PredictionSource History
</span></span><span class="line" line="7"><span>Set-PSReadLineOption -PredictionViewStyle InlineView
</span></span><span class="line" line="8"><span>Set-PSReadLineOption -Colors @{ InlinePrediction = '#666666' }
</span></span><span class="line" line="9"><span emptyLinePlaceholder>
</span></span><span class="line" line="10"><span># Intuitive Navigation
</span></span><span class="line" line="11"><span>Set-PSReadLineKeyHandler -Chord 'RightArrow' -Function AcceptSuggestion
</span></span><span class="line" line="12"><span>Set-PSReadLineKeyHandler -Chord 'Ctrl+Space' -Function MenuComplete
</span></span><span class="line" line="13"><span emptyLinePlaceholder>
</span></span><span class="line" line="14"><span># Up/Down Arrows search history matching what you've already typed
</span></span><span class="line" line="15"><span>Set-PSReadLineKeyHandler -Chord 'UpArrow' -Function HistorySearchBackward
</span></span><span class="line" line="16"><span>Set-PSReadLineKeyHandler -Chord 'DownArrow' -Function HistorySearchForward
</span></span><span class="line" line="17"><span emptyLinePlaceholder>
</span></span><span class="line" line="18"><span># ==========================================
</span></span><span class="line" line="19"><span># 2. SUBCOMMAND COMPLETIONS (Carapace Engine)
</span></span><span class="line" line="20"><span># ==========================================
</span></span><span class="line" line="21"><span>if (Get-Command carapace -ErrorAction SilentlyContinue) {
</span></span><span class="line" line="22"><span>    carapace _powershell | Out-String | Invoke-Expression
</span></span><span class="line" line="23"><span>}
</span></span><span class="line" line="24"><span emptyLinePlaceholder>
</span></span><span class="line" line="25"><span># ==========================================
</span></span><span class="line" line="26"><span># 3. FUZZY HISTORY MATCHING (FZF)
</span></span><span class="line" line="27"><span># ==========================================
</span></span><span class="line" line="28"><span>if (Get-Module -ListAvailable -Name PSFzf) {
</span></span><span class="line" line="29"><span>    Import-Module PSFzf
</span></span><span class="line" line="30"><span>    Set-PSReadLineKeyHandler -Chord 'Ctrl+r' -ScriptBlock { [PSFzf]::PostContextHistoryFzf() }
</span></span><span class="line" line="31"><span>}
</span></span><span class="line" line="32"><span emptyLinePlaceholder>
</span></span><span class="line" line="33"><span># ==========================================
</span></span><span class="line" line="34"><span># 4. BASH ALIASES & UTILITIES
</span></span><span class="line" line="35"><span># ==========================================
</span></span><span class="line" line="36"><span>function ll { Get-ChildItem -Force }
</span></span><span class="line" line="37"><span>function grep ($pattern) { $Input | Select-String -Pattern $pattern }
</span></span><span class="line" line="38"><span>if (Get-Command gsudo -ErrorAction SilentlyContinue) { Set-Alias -Name sudo -Value gsudo }
</span></span><span class="line" line="39"><span>function .. { Set-Location .. }
</span></span><span class="line" line="40"><span emptyLinePlaceholder>
</span></span><span class="line" line="41"><span>Write-Host "🚀 PowerShell environment loaded with Bash-UX engine." -ForegroundColor Cyan
</span></span></code></pre>
<style>
html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}
</style>]]></content:encoded>
            <author>no-reply@yourdomain.com (Gideon Yebei) (Gideon Yebei)</author>
        </item>
    </channel>
</rss>