<?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/">
  <channel>
    <title>Forem: Hitesh Sachdeva</title>
    <description>The latest articles on Forem by Hitesh Sachdeva (@hsachdeva9).</description>
    <link>https://forem.com/hsachdeva9</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3482268%2F3d6b9956-cdc7-4da3-acf6-46aa046e2f70.png</url>
      <title>Forem: Hitesh Sachdeva</title>
      <link>https://forem.com/hsachdeva9</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/hsachdeva9"/>
    <language>en</language>
    <item>
      <title>Release 0.4 Final: Reflecting on My Open Source Contributions for release 0.4</title>
      <dc:creator>Hitesh Sachdeva</dc:creator>
      <pubDate>Fri, 12 Dec 2025 16:14:07 +0000</pubDate>
      <link>https://forem.com/hsachdeva9/release-04-final-reflecting-on-my-open-source-contributions-for-release-04-1g3e</link>
      <guid>https://forem.com/hsachdeva9/release-04-final-reflecting-on-my-open-source-contributions-for-release-04-1g3e</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Over the past two weeks, I worked on two meaningful contributions to open-source projects. This final blog post summarizes my work, discusses the results, and reflects on what I learned throughout Release 0.4.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Project&lt;/th&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;PR Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OpenCTI&lt;/td&gt;
&lt;td&gt;#13594&lt;/td&gt;
&lt;td&gt;New Feature&lt;/td&gt;
&lt;td&gt;PR #13608 - Submitted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nextcloud Desktop&lt;/td&gt;
&lt;td&gt;#9197&lt;/td&gt;
&lt;td&gt;Bug Fix&lt;/td&gt;
&lt;td&gt;PR #9206 - Discussion with Maintainer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Contribution 1: OpenCTI - Configurable Export Timeout
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;OpenCTI's data export feature had a hardcoded 20-minute timeout. For organizations with large threat intelligence datasets, exports would fail before completing. Administrators had no way to change this without modifying the source code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; &lt;a href="https://github.com/OpenCTI-Platform/opencti/issues/13594" rel="noopener noreferrer"&gt;https://github.com/OpenCTI-Platform/opencti/issues/13594&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  My Solution
&lt;/h3&gt;

&lt;p&gt;I made the export timeout configurable through platform settings:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Added &lt;code&gt;platform_export_timeout&lt;/code&gt; field to the GraphQL schema&lt;/li&gt;
&lt;li&gt;Modified &lt;code&gt;workToExportFile()&lt;/code&gt; to read timeout from settings&lt;/li&gt;
&lt;li&gt;Implemented a fallback to 20 minutes for backward compatibility&lt;/li&gt;
&lt;li&gt;Updated all function callers to handle the async changes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Pull Request:&lt;/strong&gt; &lt;a href="https://github.com/OpenCTI-Platform/opencti/pull/13608" rel="noopener noreferrer"&gt;https://github.com/OpenCTI-Platform/opencti/pull/13608&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical Highlights
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Read timeout from platform settings with fallback&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getEntityFromCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ENTITY_TYPE_SETTINGS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;platform_export_timeout&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Default 20 minutes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Result
&lt;/h3&gt;

&lt;p&gt;The pull request is currently under review. I have responded to initial feedback and am awaiting final approval from the maintainers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contribution 2: Nextcloud Desktop - Folder Icon Fix
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;On Windows, custom folder icons were being reset every time Nextcloud synced files. Users who organized their folders with custom icons would lose them after every sync operation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; &lt;a href="https://github.com/nextcloud/desktop/issues/9197" rel="noopener noreferrer"&gt;https://github.com/nextcloud/desktop/issues/9197&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Root Cause
&lt;/h3&gt;

&lt;p&gt;Windows custom folder icons require:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;desktop.ini&lt;/code&gt; file inside the folder&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;FILE_ATTRIBUTE_SYSTEM&lt;/code&gt; flag on the folder&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Nextcloud CFAPI wrapper was overwriting file attributes during sync, losing the &lt;code&gt;FILE_ATTRIBUTE_SYSTEM&lt;/code&gt; flag.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Solution
&lt;/h3&gt;

&lt;p&gt;I modified &lt;strong&gt;cfapiwrapper.cpp&lt;/strong&gt; to preserve existing file attributes before updating placeholders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Preserve existing file attributes (especially FILE_ATTRIBUTE_SYSTEM)&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;currentAttributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetFileAttributesW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utf16&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentAttributes&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;INVALID_FILE_ATTRIBUTES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BasicInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileAttributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentAttributes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pull Request:&lt;/strong&gt; &lt;a href="https://github.com/nextcloud/desktop/pull/9206" rel="noopener noreferrer"&gt;https://github.com/nextcloud/desktop/pull/9206&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Result
&lt;/h3&gt;

&lt;p&gt;The pull request is currently under review. I have responded to initial feedback and am awaiting final approval from the maintainers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skills I Developed
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Technical Skills
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GraphQL Schema Design:&lt;/strong&gt; Learned how to extend GraphQL schemas and propagate changes through a large application&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows API Programming:&lt;/strong&gt; Gained hands-on experience with &lt;code&gt;GetFileAttributesW()&lt;/code&gt; and the Cloud Files API (CFAPI)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C++ Development:&lt;/strong&gt; Navigated and contributed to a large C++ codebase for the first time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching Systems:&lt;/strong&gt; Understood how platform settings and caching work in enterprise applications&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Soft Skills
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Code Reading:&lt;/strong&gt; Improved my ability to understand unfamiliar codebases quickly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technical Communication:&lt;/strong&gt; Wrote clear PR descriptions and responded professionally to review feedback&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project Navigation:&lt;/strong&gt; Learned to find relevant code in large repositories with hundreds of files&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenges and How I Overcame Them
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Challenge 1: Complex Build Environments
&lt;/h3&gt;

&lt;p&gt;Nextcloud Desktop required Qt6, KDE libraries, and other dependencies. Setting up the build environment on Windows was difficult.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; I focused on understanding the code thoroughly and ensuring my changes were correct based on existing patterns, even without a full local build.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 2: Understanding New APIs
&lt;/h3&gt;

&lt;p&gt;Both the Windows CFAPI and OpenCTI's caching system were completely new to me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; I spent time reading official documentation, studying existing code patterns, and tracing through function calls to build understanding.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 3: Following Project Conventions
&lt;/h3&gt;

&lt;p&gt;Each project had its own coding style, PR templates, and contribution guidelines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; I carefully read contribution guides and studied recent merged PRs to match the expected format and style.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Read before you write.&lt;/strong&gt; Understanding existing code patterns is essential before making changes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Backward compatibility matters.&lt;/strong&gt; Always provide sensible defaults so existing users are not affected.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Small changes can have big impact.&lt;/strong&gt; Both my contributions were relatively small in terms of lines of code, but they solved real problems for real users.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Open source is collaborative.&lt;/strong&gt; Engaging with maintainers and responding to feedback is just as important as writing good code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Challenge yourself.&lt;/strong&gt; Working on C++ and Windows APIs pushed me outside my comfort zone, but that is where the most learning happens.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What I Would Do Differently
&lt;/h2&gt;

&lt;p&gt;If I were starting Release 0.4 again, I would:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up build environments earlier to allow for local testing&lt;/li&gt;
&lt;li&gt;Start exploring issues sooner to have more time for complex contributions&lt;/li&gt;
&lt;li&gt;Engage more with the project communities through discussion forums or chat channels&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Release 0.4 was a significant step forward in my open-source journey. I moved beyond simple fixes to contribute a new feature and a meaningful bug fix to two established projects. The Pr's are  progressing through review.&lt;/p&gt;

&lt;p&gt;More importantly, I gained confidence in my ability to contribute to unfamiliar codebases and technologies. Whether it is JavaScript, GraphQL, C++, or Windows APIs, the core skills of reading code, understanding systems, and communicating clearly remain the same.&lt;/p&gt;

&lt;p&gt;I am proud of what I accomplished and excited to continue contributing to open source in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;OpenCTI:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Issue: &lt;a href="https://github.com/OpenCTI-Platform/opencti/issues/13594" rel="noopener noreferrer"&gt;#13594&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Pull Request: &lt;a href="https://github.com/OpenCTI-Platform/opencti/pull/13608" rel="noopener noreferrer"&gt;#13608&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Nextcloud Desktop:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Issue: &lt;a href="https://github.com/nextcloud/desktop/issues/9197" rel="noopener noreferrer"&gt;#9197&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Pull Request: &lt;a href="https://github.com/nextcloud/desktop/pull/9206" rel="noopener noreferrer"&gt;#9206&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>opensource</category>
      <category>programming</category>
      <category>learning</category>
      <category>testing</category>
    </item>
    <item>
      <title># Release 0.4 Week 2: Fixing a Windows Bug in Nextcloud Desktop Client</title>
      <dc:creator>Hitesh Sachdeva</dc:creator>
      <pubDate>Fri, 12 Dec 2025 02:09:15 +0000</pubDate>
      <link>https://forem.com/hsachdeva9/-release-04-week-2-fixing-a-windows-bug-in-nextcloud-desktop-client-28f7</link>
      <guid>https://forem.com/hsachdeva9/-release-04-week-2-fixing-a-windows-bug-in-nextcloud-desktop-client-28f7</guid>
      <description>&lt;h2&gt;
  
  
  Moving to a New Challenge
&lt;/h2&gt;

&lt;p&gt;After submitting my pull request for OpenCTI, I wanted to take on something different for the second half of Release 0.4. I decided to explore the &lt;a href="https://github.com/nextcloud" rel="noopener noreferrer"&gt;&lt;strong&gt;Nextcloud Desktop Client&lt;/strong&gt;&lt;/a&gt;, a popular open-source file synchronization tool used by millions of people worldwide.&lt;/p&gt;

&lt;p&gt;Nextcloud allows users to sync files across devices while maintaining control over their data. The desktop client is a C++ application with deep integrations into Windows, macOS, and Linux. This was a significant step outside my comfort zone since most of my experience has been with JavaScript, not C++.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bug I Found
&lt;/h2&gt;

&lt;p&gt;While browsing the Nextcloud issues, I came across &lt;strong&gt;Issue #9197&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/nextcloud/desktop/issues/9197" rel="noopener noreferrer"&gt;https://github.com/nextcloud/desktop/issues/9197&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The problem was straightforward from a user's perspective: &lt;strong&gt;custom folder icons on Windows were being reset every time Nextcloud synced files.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On Windows, users can set custom icons on folders by right-clicking → Properties → Customize → Change Icon. Many people use this to visually organize their files. But with this bug, every sync would wipe out those custom icons and replace them with the default folder icon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Root Cause
&lt;/h2&gt;

&lt;p&gt;Before fixing anything, I needed to understand how Windows handles custom folder icons. After some research, I learned that Windows uses two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A hidden &lt;code&gt;desktop.ini&lt;/code&gt; file inside the folder containing the icon path&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;FILE_ATTRIBUTE_SYSTEM&lt;/code&gt; flag set on the folder&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both must be present for the custom icon to appear. If either is missing, Windows shows the default icon.&lt;/p&gt;

&lt;p&gt;I then explored the Nextcloud codebase to find where this attribute might be getting lost. The relevant code was in:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;src/libsync/vfs/cfapi/cfapiwrapper.cpp&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This file handles the &lt;strong&gt;Windows Cloud Files API (CFAPI)&lt;/strong&gt;, which Nextcloud uses for virtual file support. I discovered that during sync operations, the code was setting file attributes to default values (&lt;code&gt;FILE_ATTRIBUTE_NORMAL&lt;/code&gt; or &lt;code&gt;FILE_ATTRIBUTE_DIRECTORY&lt;/code&gt;) without preserving existing attributes like &lt;code&gt;FILE_ATTRIBUTE_SYSTEM&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Fix
&lt;/h2&gt;

&lt;p&gt;The solution was to read the current file attributes before updating them, and preserve any existing flags. I modified three functions in &lt;code&gt;cfapiwrapper.cpp&lt;/code&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;updatePlaceholderState()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Added code to read and preserve existing attributes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Preserve existing file attributes (especially FILE_ATTRIBUTE_SYSTEM for custom folder icons)&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;currentAttributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetFileAttributesW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;reinterpret_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;wchar_t&lt;/span&gt; &lt;span class="o"&gt;*&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utf16&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentAttributes&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;INVALID_FILE_ATTRIBUTES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BasicInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileAttributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentAttributes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. &lt;code&gt;createPlaceholderInfo()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Modified to check for existing attributes before setting defaults:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;currentAttributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetFileAttributesW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;reinterpret_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;wchar_t&lt;/span&gt; &lt;span class="o"&gt;*&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utf16&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentAttributes&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;INVALID_FILE_ATTRIBUTES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;cloudEntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FsMetadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BasicInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileAttributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentAttributes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;cloudEntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FsMetadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BasicInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileAttributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fileInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isDir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;FILE_ATTRIBUTE_DIRECTORY&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FILE_ATTRIBUTE_NORMAL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. &lt;code&gt;updatePlaceholderState()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Applied the same fix to the batch placeholder creation function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges I Faced
&lt;/h2&gt;

&lt;p&gt;This contribution came with some significant challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Building the project locally was difficult.&lt;/strong&gt; Nextcloud Desktop has complex dependencies including Qt6 and KDE libraries. Setting up the build environment on Windows proved challenging.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Understanding the CFAPI.&lt;/strong&gt; The Windows Cloud Files API was completely new to me, and I had to spend time reading Microsoft documentation to understand how it works.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Navigating a large C++ codebase.&lt;/strong&gt; With hundreds of files and complex abstractions, finding the right place to make changes took patience and careful code tracing.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Current Status
&lt;/h2&gt;

&lt;p&gt;I have completed the fix and verified that the code changes are correct based on my analysis of the codebase. The changes are ready for a pull request:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Files Changed:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;src/libsync/vfs/cfapi/cfapiwrapper.cpp&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Issue Reference:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/nextcloud/desktop/issues/9197" rel="noopener noreferrer"&gt;https://github.com/nextcloud/desktop/issues/9197&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;This week taught me several valuable lessons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How Windows file attributes work at a low level, especially &lt;code&gt;FILE_ATTRIBUTE_SYSTEM&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;How the Cloud Files API (CFAPI) integrates with sync engines&lt;/li&gt;
&lt;li&gt;How to navigate and understand a large unfamiliar C++ codebase&lt;/li&gt;
&lt;li&gt;The importance of preserving system state during operations that modify files&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Looking Ahead
&lt;/h2&gt;

&lt;p&gt;My next steps are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Submit a clean pull request with proper documentation&lt;/li&gt;
&lt;li&gt;Respond to any code review feedback from maintainers&lt;/li&gt;
&lt;li&gt;Work toward getting the fix merged&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Week 2 pushed me into unfamiliar territory with C++ and Windows system programming. While it was challenging, I gained a much deeper understanding of how desktop sync applications work under the hood. Combined with my OpenCTI contribution from Week 1, I feel like I'm making meaningful progress in my open-source journey.&lt;/p&gt;

&lt;p&gt;In my final blog post, I'll share the results of both contributions and reflect on what I've learned throughout Release 0.4.&lt;/p&gt;

</description>
      <category>devjournal</category>
      <category>learning</category>
      <category>opensource</category>
      <category>cpp</category>
    </item>
    <item>
      <title>Release 0.4: Contributing a New Feature to OpenCTI - A Cyber Threat Intelligence Platform</title>
      <dc:creator>Hitesh Sachdeva</dc:creator>
      <pubDate>Fri, 12 Dec 2025 02:00:45 +0000</pubDate>
      <link>https://forem.com/hsachdeva9/release-04-contributing-a-new-feature-to-opencti-a-cyber-threat-intelligence-platform-14o1</link>
      <guid>https://forem.com/hsachdeva9/release-04-contributing-a-new-feature-to-opencti-a-cyber-threat-intelligence-platform-14o1</guid>
      <description>&lt;h2&gt;
  
  
  Finding My Way to OpenCTI
&lt;/h2&gt;

&lt;p&gt;When I started planning what I wanted to work on for Release 0.4, I knew I wanted something that would challenge me and align with my interests. I had already contributed a small UI fix to OpenCTI, but this time I wanted to understand its implementation in depth.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/OpenCTI-Platform/opencti" rel="noopener noreferrer"&gt;&lt;strong&gt;OpenCTI&lt;/strong&gt;&lt;/a&gt; stands for Open Cyber Threat Intelligence. It is an open-source platform that helps organizations collect, store, and analyze cyber-threat information. It serves as a central hub where teams gather intelligence about attackers, malware, vulnerabilities, and more to protect their systems.&lt;/p&gt;

&lt;p&gt;What drew me to this project was both the subject matter and the technology stack. OpenCTI uses JavaScript and GraphQL on the backend—technologies I'm familiar with and wanted to explore further.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discovering the Issue
&lt;/h2&gt;

&lt;p&gt;While browsing issues in the OpenCTI repository, I found &lt;strong&gt;Issue #13594&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/OpenCTI-Platform/opencti/issues/13594" rel="noopener noreferrer"&gt;https://github.com/OpenCTI-Platform/opencti/issues/13594&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The issue described a limitation with the export feature. When users exported data (like reports, indicators, or other threat intelligence), the process had a hardcoded 20-minute timeout. For large datasets, this was not enough, causing exports to fail. And administrators couldn't change the timeout without editing the source code.&lt;/p&gt;

&lt;p&gt;The request was simple: &lt;strong&gt;make the export timeout configurable through platform settings.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Issue Appealed to Me
&lt;/h2&gt;

&lt;p&gt;I chose this issue because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It was a &lt;strong&gt;feature request&lt;/strong&gt;, not just a small fix.&lt;/li&gt;
&lt;li&gt;It solved a &lt;strong&gt;real problem&lt;/strong&gt; for users handling large datasets.&lt;/li&gt;
&lt;li&gt;It had the &lt;strong&gt;right level of difficulty&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;I wanted to understand how OpenCTI handles platform settings, caching, and GraphQL schema changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Understanding the Codebase
&lt;/h2&gt;

&lt;p&gt;Before making changes, I explored how OpenCTI manages settings.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;GraphQL schema&lt;/strong&gt; defines a &lt;code&gt;Settings&lt;/code&gt; type where platform configurations live.&lt;/li&gt;
&lt;li&gt;Settings are stored in the database and accessed through a &lt;strong&gt;caching layer&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The export logic is implemented in &lt;code&gt;work.js&lt;/code&gt;, inside the &lt;code&gt;workToExportFile&lt;/code&gt; function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where the 20-minute timeout was hardcoded. Once I found that, the path forward became clear.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Implementation Approach
&lt;/h2&gt;

&lt;p&gt;My goal was to follow existing patterns and keep everything backward compatible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Steps I Took:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Added a new field in the GraphQL schema:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;platform_export_timeout&lt;/code&gt; under &lt;code&gt;Settings&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Modified the &lt;code&gt;workToExportFile&lt;/code&gt; function&lt;/strong&gt; to read the timeout from settings instead of using the hardcoded 20 minutes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Implemented a fallback:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the setting is missing, the system still uses the default 20-minute timeout so older installations continue working normally.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Updated all places where &lt;code&gt;workToExportFile&lt;/code&gt; is called&lt;/strong&gt;, since the function now needed the context and became asynchronous.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Challenges Along the Way
&lt;/h2&gt;

&lt;p&gt;The main challenges were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understanding the caching system (&lt;code&gt;getEntityFromCache&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Following OpenCTI's conventions for schema changes and code formatting.&lt;/li&gt;
&lt;li&gt;Ensuring backward compatibility.&lt;/li&gt;
&lt;li&gt;Testing different scenarios: no setting, custom timeout, and long-running exports.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Submitting the Pull Request
&lt;/h2&gt;

&lt;p&gt;After testing, I submitted &lt;strong&gt;Pull Request #13608&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/OpenCTI-Platform/opencti/pull/13608" rel="noopener noreferrer"&gt;https://github.com/OpenCTI-Platform/opencti/pull/13608&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I documented the changes, explained the implementation clearly, and linked the PR to the original issue. I also confirmed everything required in the PR template.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;Working on this feature taught me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How GraphQL schema changes flow through a large application.&lt;/li&gt;
&lt;li&gt;How platform-wide settings and caching are managed.&lt;/li&gt;
&lt;li&gt;The importance of backward compatibility in open-source projects.&lt;/li&gt;
&lt;li&gt;How to navigate and contribute effectively to a mature codebase.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Looking Ahead
&lt;/h2&gt;

&lt;p&gt;My pull request is currently awaiting review. I'm checking for feedback and ready to make improvements if needed. I plan to continue contributing to open source and exploring more complex issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This was a meaningful learning experience that helped me understand both the technical and collaborative sides of open-source development. I look forward to sharing updates on the progress of this contribution in my next blog post.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>programming</category>
      <category>learning</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>When a Bug Isn't Actually a Bug: My Kestra Contribution Story</title>
      <dc:creator>Hitesh Sachdeva</dc:creator>
      <pubDate>Sat, 06 Dec 2025 06:32:52 +0000</pubDate>
      <link>https://forem.com/hsachdeva9/when-a-bug-isnt-actually-a-bug-my-kestra-contribution-story-4nc8</link>
      <guid>https://forem.com/hsachdeva9/when-a-bug-isnt-actually-a-bug-my-kestra-contribution-story-4nc8</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;This week, I focused on a bug in &lt;a href="https://github.com/kestra-io/kestra" rel="noopener noreferrer"&gt;Kestra&lt;/a&gt;, a workflow orchestration platform. Among my various open-source contributions, this one taught me an important lesson: sometimes what looks like a bug is actually a carefully considered design decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;I found &lt;a href="https://github.com/kestra-io/kestra/issues/13101" rel="noopener noreferrer"&gt;an issue&lt;/a&gt; where the &lt;code&gt;followExecution&lt;/code&gt; API endpoint was sending empty execution objects to users. When someone called this endpoint to track a workflow execution in real-time using Server-Sent Events (SSE), the first event they got looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// First event sent - almost empty&lt;/span&gt;
&lt;span class="n"&gt;emitter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Execution&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;executionId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"start"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The object only had an ID with no actual execution data. This confused people using the SDK because they'd receive this empty object and wonder what went wrong. The issue also mentioned that if someone sent an invalid execution ID, they'd still get this fake object instead of a proper error.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Solution
&lt;/h2&gt;

&lt;p&gt;I spent time working on &lt;a href="https://github.com/kestra-io/kestra/pull/13277" rel="noopener noreferrer"&gt;a fix&lt;/a&gt; that seemed straightforward: validate the execution exists first, then send complete data. My approach was to use an &lt;code&gt;Await.until()&lt;/code&gt; method to wait up to 10 seconds for the execution to exist in the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// My approach - validate first&lt;/span&gt;
&lt;span class="nc"&gt;Execution&lt;/span&gt; &lt;span class="n"&gt;execution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Await&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;until&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;executionRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenantService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;resolveTenant&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;executionId&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;orElse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
    &lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofMillis&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
    &lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Send complete execution data&lt;/span&gt;
&lt;span class="n"&gt;emitter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;execution&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"start"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also added proper error handling that would return 404 errors for invalid execution IDs, and wrote unit tests to validate the behavior. Everything worked - the first event now contained real data instead of an empty shell.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why It Got Rejected
&lt;/h2&gt;

&lt;p&gt;The maintainer reviewed my PR and explained it didn't actually solve the problem. I was confused at first because my code clearly sent complete data. Then he pointed me to an earlier discussion where another maintainer had explained a critical constraint I missed.&lt;/p&gt;

&lt;p&gt;The empty object was intentional. It exists to prevent resource leaks.&lt;/p&gt;

&lt;p&gt;Here's the issue: Server-Sent Events work by keeping a connection open between server and client. If someone closes their browser tab before receiving any data, the server doesn't know the client disconnected. The connection just hangs there indefinitely, wasting memory and resources. These "phantom" connections can pile up and cause serious problems.&lt;/p&gt;

&lt;p&gt;The solution was to send &lt;em&gt;something&lt;/em&gt; - anything - immediately when the connection opens. This way, the server establishes the connection properly and can detect when clients disconnect. Even though it's just an empty object with an ID, it serves this important technical purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Trade-off
&lt;/h2&gt;

&lt;p&gt;This was a classic engineering trade-off:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A: Send empty data immediately&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prevents resource leaks&lt;/li&gt;
&lt;li&gt;Server can detect disconnections&lt;/li&gt;
&lt;li&gt;Confuses API users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Option B: Wait and send complete data&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better developer experience&lt;/li&gt;
&lt;li&gt;Clear, complete information&lt;/li&gt;
&lt;li&gt;Risk of phantom connections and memory leaks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a workflow orchestration platform that might handle thousands of concurrent executions, preventing resource leaks was more important than having perfect API responses. The team chose stability over convenience.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Happened Next
&lt;/h2&gt;

&lt;p&gt;After internal discussion, the team decided to close the issue. The empty object behavior would stay, and they'd just document this quirk in the SDK so developers would understand why the first event is incomplete.&lt;/p&gt;

&lt;p&gt;My PR was closed without being merged, but the maintainer took time to explain why. He could have just rejected it immediately, but instead he helped me understand the architectural reasoning.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Actually Learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;About Server-Sent Events:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I learned how SSE connections work and why connection lifecycle management matters. The immediate response pattern prevents resource leaks - something I hadn't considered when I first read the issue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;About Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not every problem has a perfect solution. Sometimes you choose the "less bad" option. In this case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;System stability vs perfect API responses&lt;/li&gt;
&lt;li&gt;Resource management vs user confusion&lt;/li&gt;
&lt;li&gt;Code fixes vs documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The team chose stability because Kestra needs to handle production workloads reliably.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;About Reading Code:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When I saw "fake empty object" in the issue description, I assumed it was obviously wrong and jumped straight to fixing it. I should have asked myself: why was it implemented this way in the first place? There's usually a reason.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;About Communication:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I asked the maintainer to clarify what I was missing, and he explained the internal discussion and decision. Sometimes issues get closed not because your code is bad, but because the team decides the current behavior is actually acceptable given the constraints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Not all "bugs" are actually bugs.&lt;/strong&gt; Sometimes weird behavior exists for good technical reasons. Ask why before assuming something is wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs are everywhere.&lt;/strong&gt; Perfect solutions rarely exist. Teams make choices based on priorities - in this case, system stability over API convenience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Closed PRs still teach you things.&lt;/strong&gt; I learned about SSE, resource management, and architectural trade-offs even though nothing merged.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context matters.&lt;/strong&gt; Maintainers understand the bigger picture - system requirements, production constraints, past problems. They might reject your fix because they know something you don't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving Forward
&lt;/h2&gt;

&lt;p&gt;This experience changed how I approach issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read the existing implementation carefully and ask why it works that way&lt;/li&gt;
&lt;li&gt;Look for comments explaining unusual patterns&lt;/li&gt;
&lt;li&gt;Ask about constraints before proposing solutions&lt;/li&gt;
&lt;li&gt;Understand that maintainers aren't being difficult - they're protecting the system&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not every contribution makes it to production, but every attempt teaches you something about real-world software development.&lt;/p&gt;

</description>
      <category>java</category>
      <category>opensource</category>
      <category>backend</category>
      <category>learning</category>
    </item>
    <item>
      <title>Fixing Canvas Export Flicker in Kaoto: My Open Source Journey</title>
      <dc:creator>Hitesh Sachdeva</dc:creator>
      <pubDate>Sat, 06 Dec 2025 06:18:55 +0000</pubDate>
      <link>https://forem.com/hsachdeva9/fixing-canvas-export-flicker-in-kaoto-my-open-source-journey-27bg</link>
      <guid>https://forem.com/hsachdeva9/fixing-canvas-export-flicker-in-kaoto-my-open-source-journey-27bg</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;When users clicked "Export as image," the entire canvas would visibly jump around, center itself, and snap back. This happened because the export function manipulated the visible canvas &lt;a href="https://github.com/KaotoIO/kaoto/issues/2750" rel="noopener noreferrer"&gt;Issue #2750&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Old approach - caused flicker&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getGraph&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// User sees this happening!&lt;/span&gt;
&lt;span class="nx"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Canvas jumps around&lt;/span&gt;
&lt;span class="nx"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Everything moves&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;exportToPng&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;surface&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Users could see all these transformations happening in real-time before the export. Not a great experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  My First Solution (That Got Rejected)
&lt;/h2&gt;

&lt;p&gt;I submitted my &lt;a href="https://github.com/KaotoIO/kaoto/pull/2766" rel="noopener noreferrer"&gt;Pull Request #2766&lt;/a&gt; with a DOM cloning approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Clone the visible canvas&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;visibleContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cloneNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fixed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-99999px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Transform and export the clone&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;toPng&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The flicker was gone! But the maintainer &lt;a href="https://github.com/lordrip" rel="noopener noreferrer"&gt;@lordrip&lt;/a&gt; explained this wasn't the right architectural approach. He wanted me to create a separate React component with its own controller that renders off-screen.&lt;/p&gt;

&lt;p&gt;I was confused. Why rebuild everything when cloning worked?&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Real Solution
&lt;/h2&gt;

&lt;p&gt;After reading &lt;a href="https://github.com/KaotoIO/kaoto/blob/main/packages/ui/src/components/Visualization/Canvas/Canvas.tsx" rel="noopener noreferrer"&gt;Canvas.tsx&lt;/a&gt;, I understood the pattern. The canvas uses PatternFly's React Topology library with a controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ControllerService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createController&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The insight: create a &lt;em&gt;second&lt;/em&gt; controller that renders invisibly. Transform that one, not the visible canvas!&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the Hidden Canvas
&lt;/h2&gt;

&lt;p&gt;I created &lt;code&gt;HiddenCanvas.tsx&lt;/code&gt; that builds its own model from entities (same way the main Canvas does):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HiddenCanvas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FunctionComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hiddenController&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
    &lt;span class="nx"&gt;ControllerService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createController&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; 
    &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Build model from entities&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CanvasNode&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CanvasEdge&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;childNodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;childEdges&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
      &lt;span class="nx"&gt;FlowService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFlowDiagram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toVizNode&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;childNodes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;childEdges&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Problems I Hit
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;TypeScript Errors:&lt;/strong&gt; I tried copying nodes from the existing controller using &lt;code&gt;toModel()&lt;/code&gt;, but this broke parent-child relationships. The fix was building a fresh model from entities using &lt;code&gt;FlowService.getFlowDiagram()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blank Exports:&lt;/strong&gt; The SVG had no width/height attributes. I had to set them manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBBox&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;padding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;width&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;padding&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;height&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;padding&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;MobX Warnings:&lt;/strong&gt; Graph transformations needed to be wrapped in &lt;code&gt;action()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;action&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;hiddenGraph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;hiddenGraph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;hiddenGraph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Timing Issues:&lt;/strong&gt; I had to wait for the layout to complete before exporting. Instead of a blind timeout, I checked if nodes were actually positioned.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;The maintainer was right. DOM cloning was a hack. Creating a proper component:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is more maintainable&lt;/li&gt;
&lt;li&gt;Follows React patterns&lt;/li&gt;
&lt;li&gt;Works better with the library's architecture&lt;/li&gt;
&lt;li&gt;Teaches you how the system actually works&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Technical skills gained:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React context providers and controllers&lt;/li&gt;
&lt;li&gt;MobX state management&lt;/li&gt;
&lt;li&gt;SVG manipulation&lt;/li&gt;
&lt;li&gt;Async timing with Promises&lt;/li&gt;
&lt;li&gt;TypeScript type system&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Results and What I Actually Learned
&lt;/h2&gt;

&lt;p&gt;The final implementation completely eliminates the flicker. The visible canvas stays in place while the export happens in the background using the hidden controller. Users can now export their diagrams without any visual disruption.&lt;/p&gt;

&lt;p&gt;But looking back, I understand why the maintainer pushed for the proper solution. DOM cloning might work, but it doesn't teach you anything about how the system actually works. By building a proper React component with its own controller, I learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How React's context and provider pattern works in complex applications&lt;/li&gt;
&lt;li&gt;Why separating concerns matters (visible canvas vs export canvas)&lt;/li&gt;
&lt;li&gt;How to build models from data sources instead of copying existing instances&lt;/li&gt;
&lt;li&gt;The importance of understanding the library's architecture before writing code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The biggest lesson was that in open-source, it's not just about making something work - it's about making it work &lt;em&gt;the right way&lt;/em&gt; so other developers can understand and maintain it later. The maintainer could have accepted my quick fix, but he took the time to explain why a better approach existed. That's what made this a real learning experience instead of just a bug fix.&lt;/p&gt;

</description>
      <category>react</category>
      <category>opensource</category>
      <category>typescript</category>
      <category>learning</category>
    </item>
    <item>
      <title>Releasing share-my-repo: A Journey from Local Code to PyPI</title>
      <dc:creator>Hitesh Sachdeva</dc:creator>
      <pubDate>Sat, 22 Nov 2025 04:15:36 +0000</pubDate>
      <link>https://forem.com/hsachdeva9/releasing-share-my-repo-a-journey-from-local-code-to-pypi-412p</link>
      <guid>https://forem.com/hsachdeva9/releasing-share-my-repo-a-journey-from-local-code-to-pypi-412p</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Over the past few weeks, I worked on &lt;strong&gt;&lt;code&gt;share-my-repo&lt;/code&gt;&lt;/strong&gt;, a CLI tool designed to package Git repositories into a format suitable for analysis by Large Language Models (LLMs). The goal was to create a user-friendly, configurable tool that could extract repository metadata, and generate structured file outputs.&lt;/p&gt;

&lt;p&gt;Once the tool was ready, the next step was to &lt;strong&gt;release it&lt;/strong&gt; so that others could easily install and use it via a package registry. This post walks through my entire release process, the challenges I faced, lessons learned, and the results of user testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing a Release Tool and Package Registry
&lt;/h2&gt;

&lt;p&gt;I decided to use Python’s standard packaging tools along with &lt;strong&gt;PyPI&lt;/strong&gt; as the package registry. Specifically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Build &amp;amp; Packaging:&lt;/strong&gt; &lt;code&gt;setuptools&lt;/code&gt; + &lt;code&gt;wheel&lt;/code&gt; + &lt;code&gt;setuptools_scm&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distribution/Upload:&lt;/strong&gt; &lt;code&gt;twine&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Registry:&lt;/strong&gt; &lt;a href="https://pypi.org/" rel="noopener noreferrer"&gt;PyPI (Python Package Index)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tools are standard in the Python ecosystem and widely used for publishing Python packages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Release
&lt;/h2&gt;

&lt;p&gt;The release process involved several steps:&lt;/p&gt;

&lt;h3&gt;
  
  
  Updating Version
&lt;/h3&gt;

&lt;p&gt;I updated the &lt;code&gt;pyproject.toml&lt;/code&gt; to bump the version from &lt;code&gt;1.0.0&lt;/code&gt; to &lt;code&gt;1.2.0&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the Package
&lt;/h3&gt;

&lt;p&gt;I created a source distribution and a wheel using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Produced:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dist/share_my_repo-1.2.0.tar.gz
dist/share_my_repo-1.2.0-py3-none-any.whl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Uploading to PyPI
&lt;/h3&gt;

&lt;p&gt;I uploaded the distributions using twine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; twine upload dist/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tagging the Release in GitHub
&lt;/h3&gt;

&lt;p&gt;After pushing my code, I created a Git tag for the release:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git tag v1.2.0
git push origin main &lt;span class="nt"&gt;--tags&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This made the release visible on GitHub under Releases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;pyproject.toml&lt;/code&gt; is powerful:&lt;/strong&gt; Switching from &lt;code&gt;setup.py&lt;/code&gt; to &lt;code&gt;pyproject.toml&lt;/code&gt; made the build process cleaner and more modern.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;User testing highlights documentation gaps:&lt;/strong&gt; Even small missing instructions in &lt;code&gt;README.md&lt;/code&gt; caused confusion for first-time users.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;One "aha!" moment was realizing that &lt;strong&gt;&lt;code&gt;setuptools_scm&lt;/code&gt; can auto-generate version numbers from Git tags&lt;/strong&gt;, which makes managing releases much easier in the long run.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Changes Required for Package Format
&lt;/h2&gt;

&lt;p&gt;To release &lt;code&gt;share-my-repo&lt;/code&gt; successfully, I had to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Move from a &lt;code&gt;setup.py&lt;/code&gt; file to a &lt;code&gt;pyproject.toml&lt;/code&gt; configuration.
&lt;/li&gt;
&lt;li&gt;Organize source code under a &lt;code&gt;src&lt;/code&gt; folder to comply with best practices.
&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;[project.scripts]&lt;/code&gt; entries in &lt;code&gt;pyproject.toml&lt;/code&gt; for CLI functionality.
&lt;/li&gt;
&lt;li&gt;Ensure dependencies were correctly listed under &lt;code&gt;[project.dependencies]&lt;/code&gt; and &lt;code&gt;[project.optional-dependencies]&lt;/code&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These changes were mostly &lt;strong&gt;structural&lt;/strong&gt; and did not require altering the core functionality of the tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  User Testing
&lt;/h2&gt;

&lt;p&gt;I conducted a user testing session with a fellow classmate. Key observations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They were able to &lt;strong&gt;install the package from PyPI&lt;/strong&gt; without any issues.
&lt;/li&gt;
&lt;li&gt;Confusion arose when using optional flags like &lt;code&gt;--preview&lt;/code&gt; and &lt;code&gt;--use-gitignore&lt;/code&gt; because the README did not clearly explain the default behavior.
&lt;/li&gt;
&lt;li&gt;After observing them, I updated the README to include &lt;strong&gt;examples with common flag combinations&lt;/strong&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This session was invaluable because it allowed me to &lt;strong&gt;fix documentation issues&lt;/strong&gt; before sharing the package widely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation &amp;amp; Usage
&lt;/h2&gt;

&lt;p&gt;After release, users can install the package directly from PyPI &lt;a href="https://pypi.org/project/share-my-repo/" rel="noopener noreferrer"&gt;release&lt;/a&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  Install via PyPI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;share-my-repo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install from Github
&lt;/h3&gt;

&lt;p&gt;If you want to install the package directly from GitHub:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clone the repository:&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  For End Users
&lt;/h3&gt;

&lt;p&gt;Clone and install the package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# clone the repository &lt;/span&gt;
git clone https://github.com/yourusername/share-my-repo.git
&lt;span class="nb"&gt;cd &lt;/span&gt;share-my-repo

&lt;span class="c"&gt;# install dependencies&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  For Development / Contributors
&lt;/h3&gt;

&lt;p&gt;Install in editable mode to make code changes without reinstalling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# clone the repository &lt;/span&gt;
git clone https://github.com/yourusername/share-my-repo.git
&lt;span class="nb"&gt;cd &lt;/span&gt;share-my-repo

&lt;span class="c"&gt;# install dependencies&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;share-my-repo &lt;span class="o"&gt;[&lt;/span&gt;OPTIONS] &lt;span class="o"&gt;[&lt;/span&gt;PATHS...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Some Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Process the current directory and print Markdown to console:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;share-my-repo &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Process a repository and save output to a file:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;share-my-repo &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; repo_context.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Include only Python and JavaScript files:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;share-my-repo &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--include&lt;/span&gt; &lt;span class="s2"&gt;"*.py,*.js"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Output as JSON:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;share-my-repo &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; json &lt;span class="nt"&gt;--output&lt;/span&gt; repo.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Releasing &lt;code&gt;share-my-repo&lt;/code&gt; taught me key lessons:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Versioning is crucial&lt;/strong&gt;: Always increment versions to avoid PyPI upload errors.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modern packaging works&lt;/strong&gt;: &lt;code&gt;pyproject.toml&lt;/code&gt; makes builds cleaner and easier to manage.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clear documentation matters&lt;/strong&gt;: User testing revealed gaps and helped improve the README.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automation helps&lt;/strong&gt;: &lt;code&gt;setuptools_scm&lt;/code&gt; simplifies version management.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User perspective is important&lt;/strong&gt;: Observing real users showed how to make CLI options more intuitive.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, this experience improved my understanding of Python packaging, release workflows, and creating user-friendly documentation.&lt;/p&gt;

</description>
      <category>release</category>
      <category>programming</category>
      <category>opensource</category>
      <category>learning</category>
    </item>
    <item>
      <title>Setting Up CI, Automated Testing, and Linting for My share-my-repo Project</title>
      <dc:creator>Hitesh Sachdeva</dc:creator>
      <pubDate>Fri, 14 Nov 2025 15:07:28 +0000</pubDate>
      <link>https://forem.com/hsachdeva9/setting-up-ci-automated-testing-and-linting-for-my-share-my-repo-project-2caj</link>
      <guid>https://forem.com/hsachdeva9/setting-up-ci-automated-testing-and-linting-for-my-share-my-repo-project-2caj</guid>
      <description>&lt;p&gt;This week's lab focused on managing project complexity using Continuous Integration (CI), automated testing, reproducible environments, and code quality tools. I applied these concepts to my own project, &lt;a href="https://github.com/hsachdeva9/share-my-repo" rel="noopener noreferrer"&gt;share-my-repo&lt;/a&gt;, and also collaborated on a partner's C++ project, &lt;a href="https://github.com/OleksandraKordonets/Repository-Context-Packager" rel="noopener noreferrer"&gt;Repository-Context-manager&lt;/a&gt;. Through the process, I learned not just how CI works, but also how it improves collaboration, reliability, and development workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up GitHub Actions CI in My Python Project
&lt;/h2&gt;

&lt;p&gt;To ensure my project always stays in a working state, I added a GitHub Actions CI workflow. The goal was simple: whenever someone pushes or opens a pull request, the code should automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install the environment&lt;/li&gt;
&lt;li&gt;Run linting&lt;/li&gt;
&lt;li&gt;Run all tests&lt;/li&gt;
&lt;li&gt;Show test coverage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I created a &lt;code&gt;.github/workflows/ci.yml&lt;/code&gt; file that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Triggers on every push or PR to main&lt;/li&gt;
&lt;li&gt;Sets up Python 3.11&lt;/li&gt;
&lt;li&gt;Installs pytest, pytest-cov, flake8, and black&lt;/li&gt;
&lt;li&gt;Runs linters before tests&lt;/li&gt;
&lt;li&gt;Generates a coverage report on the terminal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gave me a fully automated pipeline. I could visibly see CI results on every pull request, which made the entire development process more professional and error-free. Watching GitHub Actions run the workflow and report results felt like working on a real production project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing CI Using My Own Pull Request
&lt;/h2&gt;

&lt;p&gt;To verify that CI was truly working, I followed the workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Created a new branch — &lt;code&gt;add-more-tests&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Wrote additional unit tests for modules that had low coverage.&lt;/li&gt;
&lt;li&gt;Pushed the branch and opened a pull request against main.&lt;/li&gt;
&lt;li&gt;Observed GitHub Actions immediately fire up and begin running all steps.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I intentionally broke one of my functions to see how CI would behave. As expected, the tests failed and the pull request turned red. This highlighted how powerful CI is: no broken code can ever reach the main branch.&lt;/p&gt;

&lt;p&gt;After fixing the bug, pushing again, and seeing CI pass, I gained confidence in using automated testing as a safeguard. This cycle of fail → inspect → fix → pass felt very satisfying and realistic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Collaborating and Adding Tests to a Partner's C++ Project
&lt;/h2&gt;

&lt;p&gt;My partner's project was completely different from mine — a C++ repository using Catch2 for testing. At first, I struggled to understand the README, so I reached out to the maintainer on Teams. After they clarified the instructions, I was able to set everything up and start working on the test.&lt;/p&gt;

&lt;p&gt;Their workflow felt more complex than my Python setup because it required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compiling test files manually&lt;/li&gt;
&lt;li&gt;Understanding multi-file C++ structures&lt;/li&gt;
&lt;li&gt;Running tests with Catch2&lt;/li&gt;
&lt;li&gt;Using a CI pipeline built around &lt;code&gt;g++&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even though it was challenging, contributing to a project in a different language taught me a lot. I learned to slow down, carefully read unfamiliar code, and adapt to a new build system. Once I wrote my test case and opened the pull request, their CI passed successfully. &lt;a href="https://github.com/OleksandraKordonets/Repository-Context-Packager/pull/18" rel="noopener noreferrer"&gt;PR&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Overall, it felt like collaborating on a real open-source project, and it helped me understand how cross-language teamwork works in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Linting + Formatting: flake8 and black
&lt;/h2&gt;

&lt;p&gt;As an optional enhancement, I integrated flake8 and black into my project. These tools help maintain code quality by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;flake8&lt;/strong&gt; → catching unused variables, undefined names, and style problems&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;black&lt;/strong&gt; → automatically formatting the code to a consistent style&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I added both tools to the dev dependencies in &lt;code&gt;setup.py&lt;/code&gt; and updated my CI workflow to run them before tests. This created a nice structure:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;flake8 checks for actual errors&lt;/li&gt;
&lt;li&gt;black --check ensures formatting consistency&lt;/li&gt;
&lt;li&gt;pytest runs only if both linters pass&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This improved the readability of my project and prevented small bugs that often go unnoticed. It also taught me that CI is not just about testing — it's about enforcing standards across the whole team.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned About Continuous Integration
&lt;/h2&gt;

&lt;p&gt;After setting up everything, working with two repositories, and experimenting with failures, I now have a solid understanding of CI's importance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CI acts like a 24/7 automated reviewer, catching issues instantly.&lt;/li&gt;
&lt;li&gt;It removes the risk of forgetting to run tests locally.&lt;/li&gt;
&lt;li&gt;It provides detailed logs that make debugging very transparent.&lt;/li&gt;
&lt;li&gt;It enforces consistency in both functionality and style.&lt;/li&gt;
&lt;li&gt;It helps maintain a healthy main branch that always works.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, CI transformed my project into something more professional and maintainable. It made the testing workflow feel more natural, especially when working with branches and pull requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Reflection
&lt;/h2&gt;

&lt;p&gt;This lab tied together all the concepts from earlier weeks: testing, automation, collaboration, and reproducibility. By setting up CI, adding new tests, linting my code, and contributing to a partner's project, I gained hands-on experience with real software engineering workflows.&lt;/p&gt;

&lt;p&gt;CI is no longer just a concept; I've now seen how developers rely on it every day to protect their projects from mistakes. It made me more confident in working with branching strategies, GitHub Actions, and cross-language testing environments.&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>opensource</category>
      <category>programming</category>
    </item>
    <item>
      <title>How I Added Testing to share-my-repo Project and Learned a Lot</title>
      <dc:creator>Hitesh Sachdeva</dc:creator>
      <pubDate>Thu, 06 Nov 2025 22:23:24 +0000</pubDate>
      <link>https://forem.com/hsachdeva9/how-i-added-testing-to-share-my-repo-project-and-learned-a-lot-2f03</link>
      <guid>https://forem.com/hsachdeva9/how-i-added-testing-to-share-my-repo-project-and-learned-a-lot-2f03</guid>
      <description>&lt;p&gt;Testing is an important part of building software, but many developers, including me, sometimes skip it. In this post, I want to share how I added tests to my Python project, the tools I used, how I set them up, and what I learned along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools I Chose for Testing
&lt;/h2&gt;

&lt;p&gt;For my project, I decided to use &lt;strong&gt;pytest&lt;/strong&gt; as my main testing framework and &lt;strong&gt;pytest-cov&lt;/strong&gt; for code coverage because they are widely used, easy to set up, and provide clear feedback on test results and coverage.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.pytest.org/en/latest/" rel="noopener noreferrer"&gt;pytest&lt;/a&gt;&lt;/strong&gt;: pytest is a popular Python testing framework. It’s easy to use, supports simple tests, and has many features like fixtures and parameterized tests.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://pytest-cov.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;pytest-cov&lt;/a&gt;&lt;/strong&gt;: This plugin works with pytest to show which parts of your code are covered by tests. It helps you find which lines are being missed.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also used &lt;strong&gt;pytest-watch (ptw)&lt;/strong&gt; to automatically run tests whenever I make changes. This makes development faster and helps catch problems immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Testing in My Project
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Install dependencies:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   pip &lt;span class="nb"&gt;install &lt;/span&gt;pytest pytest-cov pytest-watch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project Organization&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I kept the source code in &lt;code&gt;src/&lt;/code&gt; and tests in &lt;code&gt;tests/&lt;/code&gt;. Each file in the source has a corresponding test file, e.g., &lt;code&gt;file_processor.py&lt;/code&gt; has &lt;code&gt;tests/test_file_processor.py&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Writing Tests&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I created test classes and methods for each function. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestNormalizePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setup_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;processor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FileProcessor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_normalize_path_basic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;Users&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;Example&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;File.TXT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;processor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_normalize_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;c:/users/example/file.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Running Tests&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pytest &lt;span class="nt"&gt;-v&lt;/span&gt;  &lt;span class="c"&gt;# run all tests&lt;/span&gt;
pytest &lt;span class="nt"&gt;-v&lt;/span&gt; tests/test_file_processor.py  &lt;span class="c"&gt;# run a single test file&lt;/span&gt;
pytest &lt;span class="nt"&gt;-v&lt;/span&gt; tests/test_file_processor.py::TestNormalizePath::test_normalize_path_basic  &lt;span class="c"&gt;# run a single test&lt;/span&gt;
ptw  &lt;span class="c"&gt;# watch for changes and run tests automatically&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Check Code Coverage&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pytest &lt;span class="nt"&gt;--cov&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;src/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also made scripts (&lt;code&gt;scripts/test.sh&lt;/code&gt; for Linux/macOS and &lt;code&gt;scripts/test.bat&lt;/code&gt; for Windows) so anyone can run tests and see coverage easily.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Think like a pessimist:&lt;/strong&gt; Writing tests made me consider all edge cases, like empty inputs or file errors.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Found hidden bugs:&lt;/strong&gt; I found paths that didn’t normalize correctly on Windows and cases where the code failed if a file was outside the root directory.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Small tests are better:&lt;/strong&gt; Writing focused tests per function made debugging much easier.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My Takeaways
&lt;/h2&gt;

&lt;p&gt;Before this project, I rarely wrote automated tests. I mostly ran scripts and checked results manually. Now, I see that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tests prevent bugs when adding new features.
&lt;/li&gt;
&lt;li&gt;Coverage reports show what needs more testing.
&lt;/li&gt;
&lt;li&gt;Tests make the project easier for others to contribute to.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I plan to write tests for all my future Python projects. Adding code coverage and watch mode has improved my workflow and code quality a lot.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>opensource</category>
      <category>programming</category>
      <category>pytest</category>
    </item>
    <item>
      <title>Wrapping Up Hacktoberfest 2025</title>
      <dc:creator>Hitesh Sachdeva</dc:creator>
      <pubDate>Sat, 01 Nov 2025 03:01:47 +0000</pubDate>
      <link>https://forem.com/hsachdeva9/wrapping-up-hacktoberfest-2025-1766</link>
      <guid>https://forem.com/hsachdeva9/wrapping-up-hacktoberfest-2025-1766</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;October has been an amazing month! This was my first time participating in Hacktoberfest, and the experience has been both exciting and educational. Over the past month, I contributed to multiple open-source projects, explored how large codebases are maintained, learned the processes behind Pull Requests (PRs), and gained confidence in making my work accepted by project maintainers.&lt;/p&gt;

&lt;p&gt;Hacktoberfest taught me how open-source communities collaborate, how projects are structured, and the standards and measures maintainers follow to keep code clean and stable. Most importantly, it reinforced that contributing to open source isn’t just about coding, it’s about learning, communication, and teamwork.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Contributions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  NESY-Engine
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; &lt;a href="https://github.com/tylerapear/NESY-Engine/issues/61" rel="noopener noreferrer"&gt;#61&lt;/a&gt; &lt;strong&gt;Pull Request:&lt;/strong&gt; &lt;a href="https://github.com/tylerapear/NESY-Engine/pull/68" rel="noopener noreferrer"&gt;#68&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I started small with NESY-Engine by improving the &lt;strong&gt;documentation of the HUD class&lt;/strong&gt;. I added class summaries, detailed constructor explanations, property descriptions, and usage examples. This made it easier for developers to understand and use the HUD class.&lt;/p&gt;

&lt;p&gt;Starting with a documentation task taught me the value of &lt;strong&gt;beginning small&lt;/strong&gt;, building confidence, and understanding project structure before attempting more complex contributions. It also showed me the importance of &lt;strong&gt;clear, structured documentation&lt;/strong&gt; for open-source projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  OpenCTI
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; &lt;a href="https://github.com/OpenCTI-Platform/opencti/issues/12824" rel="noopener noreferrer"&gt;#12824&lt;/a&gt;  &lt;strong&gt;Pull Request:&lt;/strong&gt; &lt;a href="https://github.com/OpenCTI-Platform/opencti/pull/12967" rel="noopener noreferrer"&gt;#12967&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This was a small &lt;strong&gt;UI fix&lt;/strong&gt;. The Playbook update drawer was incorrectly displaying &lt;code&gt;"Update a decay rule"&lt;/code&gt; instead of &lt;code&gt;"Update a playbook"&lt;/code&gt;. I corrected the title in &lt;code&gt;PlaybookEdition.tsx&lt;/code&gt;, ensuring it followed the proper i18n translation structure.&lt;/p&gt;

&lt;p&gt;This contribution showed me that &lt;strong&gt;even small fixes matter&lt;/strong&gt;, especially for user clarity, and taught me the importance of paying attention to &lt;strong&gt;UI details and consistency&lt;/strong&gt; in a large project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kestra
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; &lt;a href="https://github.com/kestra-io/kestra/issues/12090" rel="noopener noreferrer"&gt;#12090&lt;/a&gt; &lt;strong&gt;Pull Request:&lt;/strong&gt; &lt;a href="https://github.com/kestra-io/kestra/pull/12148" rel="noopener noreferrer"&gt;#12148&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Kestra, the Pebble &lt;code&gt;chunk&lt;/code&gt; filter was throwing type-casting exceptions when integers were used instead of Longs. I updated the filter to handle any &lt;code&gt;Number&lt;/code&gt; type, added runtime checks, and wrote unit tests to confirm proper behavior.&lt;/p&gt;

&lt;p&gt;This was a more &lt;strong&gt;challenging fix&lt;/strong&gt; that taught me about &lt;strong&gt;Java type-casting&lt;/strong&gt;, &lt;strong&gt;workflow logic&lt;/strong&gt;, and the &lt;strong&gt;importance of writing robust unit tests&lt;/strong&gt;. Successfully getting this PR merged boosted my confidence immensely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kaoto
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; &lt;a href="https://github.com/KaotoIO/kaoto/issues/2637" rel="noopener noreferrer"&gt;#2637&lt;/a&gt; &lt;strong&gt;Pull Request:&lt;/strong&gt; &lt;a href="https://github.com/KaotoIO/kaoto/pull/2669" rel="noopener noreferrer"&gt;#2669&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I fixed drag-and-drop issues in nested workflow nodes by correcting component logic and testing different container setups. Though smaller in scope, this fix improved &lt;strong&gt;user experience&lt;/strong&gt; significantly.&lt;/p&gt;

&lt;p&gt;Contributing to Kaoto taught me that &lt;strong&gt;even minor UI improvements matter&lt;/strong&gt; and that understanding the tool you work on makes contributions easier and more meaningful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reflections and Learnings
&lt;/h2&gt;

&lt;p&gt;Looking back over October:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I contributed to &lt;strong&gt;four projects&lt;/strong&gt; and had &lt;strong&gt;all my PRs merged&lt;/strong&gt;, which was incredibly satisfying.
&lt;/li&gt;
&lt;li&gt;I learned how &lt;strong&gt;open-source projects are maintained&lt;/strong&gt;, including testing standards, code reviews, and collaboration practices.
&lt;/li&gt;
&lt;li&gt;My confidence in reading complex codebases, debugging issues, and making contributions accepted has grown tremendously.
&lt;/li&gt;
&lt;li&gt;I realized that &lt;strong&gt;communication, asking questions, and submitting thoughtful PRs&lt;/strong&gt; are just as important as writing code.
&lt;/li&gt;
&lt;li&gt;Contributing to projects I actually use (like Kaoto) or widely-used platforms (like Kestra and OpenCTI) made the experience more rewarding.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hacktoberfest showed me that open source is not just about contributing code,it’s about &lt;strong&gt;learning, exploring, and growing as a developer&lt;/strong&gt;. I plan to continue contributing to open source in the future, and this first Hacktoberfest has motivated me to take on bigger and more complex projects.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>opensource</category>
      <category>hacktoberfest</category>
      <category>learning</category>
    </item>
    <item>
      <title>Hacktoberfest Week 4 – Improving DataMapper Logic in Kaoto</title>
      <dc:creator>Hitesh Sachdeva</dc:creator>
      <pubDate>Fri, 31 Oct 2025 15:57:57 +0000</pubDate>
      <link>https://forem.com/hsachdeva9/hacktoberfest-week-4-improving-datamapper-logic-in-kaoto-1l14</link>
      <guid>https://forem.com/hsachdeva9/hacktoberfest-week-4-improving-datamapper-logic-in-kaoto-1l14</guid>
      <description>&lt;p&gt;As Hacktoberfest comes to an end, I decided to take on one more interesting challenge, this time in the &lt;strong&gt;Kaoto&lt;/strong&gt; project. Kaoto is an open-source low-code integration platform that allows users to visually design and configure integrations. It’s built on top of &lt;strong&gt;Apache Camel&lt;/strong&gt;, which makes it powerful but also quite complex.&lt;/p&gt;

&lt;p&gt;After spending my previous week working on a core backend issue in Kestra, I wanted to dive deeper into something that touched &lt;strong&gt;core logic and UI behavior&lt;/strong&gt; at the same time. I chose to work on a &lt;strong&gt;drag-and-drop (DnD) bug&lt;/strong&gt; in Kaoto’s &lt;strong&gt;DataMapper UI&lt;/strong&gt;, a component that lets users visually map data sources to targets.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Issue
&lt;/h2&gt;

&lt;p&gt;The problem &lt;a href=""&gt;Issue #2637&lt;/a&gt; was that users could &lt;strong&gt;drag and drop a primitive source Body (without a schema)&lt;/strong&gt; and create a mapping. This looked fine in the UI, but it actually caused runtime failures because the backend engine (&lt;code&gt;camel-xslt-saxon&lt;/code&gt;) expected the body to be an &lt;strong&gt;XML Document&lt;/strong&gt;, not a primitive type.&lt;/p&gt;

&lt;p&gt;So in short, users could make an invalid mapping that would always fail later. My goal was to &lt;strong&gt;prevent this from happening at the UI level&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Approach
&lt;/h2&gt;

&lt;p&gt;At first, I tried to fix the issue by checking for primitive types and blocking the mapping when users tried to drop it. However, the project maintainer suggested a cleaner and more reliable approach — &lt;strong&gt;disable the drag-and-drop action altogether&lt;/strong&gt; for primitive source bodies.&lt;/p&gt;

&lt;p&gt;This was a great learning moment. Instead of fixing the problem after it happens, it’s better to prevent it from happening in the first place, a core principle of good UX and system design.&lt;/p&gt;

&lt;p&gt;So, I updated the &lt;code&gt;NodeContainer.tsx&lt;/code&gt; component to &lt;strong&gt;skip attaching the &lt;code&gt;DnDContainer&lt;/code&gt;&lt;/strong&gt; for primitive source body nodes. This ensured that users simply &lt;strong&gt;could not drag&lt;/strong&gt; them at all, while still allowing parameters (even primitive ones) to be draggable and valid.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix
&lt;/h2&gt;

&lt;p&gt;The final fix included &lt;a href="https://github.com/KaotoIO/kaoto/pull/2669" rel="noopener noreferrer"&gt;PR&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Disabling drag-and-drop for &lt;strong&gt;primitive source Body&lt;/strong&gt; nodes.
&lt;/li&gt;
&lt;li&gt;Keeping &lt;strong&gt;Parameters&lt;/strong&gt; draggable even if they’re primitive.
&lt;/li&gt;
&lt;li&gt;Refactoring the code to use a more readable flag (&lt;code&gt;hasDnD&lt;/code&gt; instead of &lt;code&gt;noDnD&lt;/code&gt;) for clarity. &lt;/li&gt;
&lt;li&gt;Testing all drag-and-drop actions to ensure &lt;strong&gt;no regressions&lt;/strong&gt; or console errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once I submitted the PR, the maintainers reviewed it quickly. After a few small suggestions (like simplifying logic and improving flag naming), the changes were approved and merged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges &amp;amp; Learnings
&lt;/h2&gt;

&lt;p&gt;This contribution felt different from my earlier ones. It wasn’t just about fixing a bug, it was about &lt;strong&gt;understanding core UI logic&lt;/strong&gt; and improving how users interact with the system.&lt;/p&gt;

&lt;p&gt;Here’s what I learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How &lt;strong&gt;drag-and-drop logic&lt;/strong&gt; is implemented in React-based UIs.
&lt;/li&gt;
&lt;li&gt;Why &lt;strong&gt;preventing invalid user actions early&lt;/strong&gt; leads to better software design.
&lt;/li&gt;
&lt;li&gt;How to interpret and implement &lt;strong&gt;maintainer feedback&lt;/strong&gt; effectively.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reflection
&lt;/h2&gt;

&lt;p&gt;This last week of Hacktoberfest has been a great wrap-up. I started the month working on documentation in &lt;strong&gt;HUD&lt;/strong&gt;, then explored backend issues in large systems like &lt;strong&gt;Kestra&lt;/strong&gt;, and finally ended it by improving UI logic in &lt;strong&gt;Kaoto&lt;/strong&gt; — connecting both worlds. &lt;/p&gt;

&lt;p&gt;Every PR taught me something new. The whole experience reminded me that open-source is not just about writing code, it’s about &lt;strong&gt;learning, collaborating, and growing as a developer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Hacktoberfest 2025 has definitely been a crazy but rewarding experience. I’m excited to continue contributing to open source beyond October and keep pushing myself toward more challenging issues in the future.&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>programming</category>
      <category>opensource</category>
      <category>hacktoberfest</category>
    </item>
    <item>
      <title>Understanding the Gitignore Feature in Repomix: A Technical Deep Dive</title>
      <dc:creator>Hitesh Sachdeva</dc:creator>
      <pubDate>Wed, 29 Oct 2025 04:36:10 +0000</pubDate>
      <link>https://forem.com/hsachdeva9/understanding-the-gitignore-feature-in-repomix-a-technical-deep-dive-4lf</link>
      <guid>https://forem.com/hsachdeva9/understanding-the-gitignore-feature-in-repomix-a-technical-deep-dive-4lf</guid>
      <description>&lt;p&gt;Repomix is a powerful tool designed to search and filter files in a repository, packaging them for AI consumption. One key feature that stood out during my code exploration is its &lt;strong&gt;handling of &lt;code&gt;.gitignore&lt;/code&gt; files&lt;/strong&gt;. In this blog post, I’ll explain how this feature works, how the code is structured, what I learned, and my strategy for understanding it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Feature Does
&lt;/h2&gt;

&lt;p&gt;The Gitignore feature allows Repomix to &lt;strong&gt;ignore files and directories&lt;/strong&gt; that are listed in &lt;code&gt;.gitignore&lt;/code&gt; or other ignore files. This ensures that temporary, build, or dependency files (like &lt;code&gt;node_modules&lt;/code&gt;, test files, or log files) are not included in the search results or final package.&lt;/p&gt;

&lt;p&gt;In short, this feature ensures that &lt;strong&gt;only relevant files are processed&lt;/strong&gt;, which is critical for performance and accuracy.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the Feature Works
&lt;/h2&gt;

&lt;p&gt;The implementation is primarily found in &lt;a href="https://github.com/yamadashy/repomix/blob/main/src/core/file/fileSearch.ts" rel="noopener noreferrer"&gt;src/core/file/fileSearch.ts&lt;/a&gt;. The process can be summarized in these steps:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Load Ignore Patterns from Git
&lt;/h3&gt;

&lt;p&gt;Repomix gathers patterns from multiple sources, including &lt;code&gt;.git/info/exclude&lt;/code&gt; when &lt;code&gt;useGitignore&lt;/code&gt; is enabled in the &lt;code&gt;config&lt;/code&gt; file :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ignore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useGitignore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;excludeFilePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.git&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;info&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exclude&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;excludeFileContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;excludeFilePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;excludePatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseIgnoreContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;excludeFileContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pattern&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;excludePatterns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;ignorePatterns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;.git/info/exclude&lt;/code&gt;&lt;/strong&gt;: Contains local ignore rules for the repository.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;config.ignore.useGitignore&lt;/code&gt;&lt;/strong&gt;: A flag in the Repomix configuration to enable &lt;code&gt;.gitignore&lt;/code&gt; support.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Patterns are parsed line by line, ignoring empty lines or comments (#)&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parseIgnoreContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;trimmedLine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trimmedLine&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;trimmedLine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trimmedLine&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Respect &lt;code&gt;.gitignore&lt;/code&gt; During File Search
&lt;/h3&gt;

&lt;p&gt;Once ignore patterns are collected, Repomix uses &lt;code&gt;globby&lt;/code&gt; to search files while excluding ignored ones:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filePaths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;globby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;includePatterns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rootDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;ignore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;adjustedIgnorePatterns&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;ignoreFiles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;ignoreFilePatterns&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// includes '**/.gitignore' if enabled&lt;/span&gt;
  &lt;span class="na"&gt;onlyFiles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;followSymbolicLinks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;ignore&lt;/code&gt;&lt;/strong&gt;: Contains all patterns, including &lt;code&gt;.gitignore&lt;/code&gt; rules from &lt;code&gt;.git/info/exclude&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;ignoreFiles&lt;/code&gt;&lt;/strong&gt;: Optionally tells &lt;code&gt;globby&lt;/code&gt; to ignore the ignore files themselves (&lt;code&gt;.gitignore&lt;/code&gt; or &lt;code&gt;.repomixignore&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures that &lt;code&gt;.gitignore&lt;/code&gt; rules are actively applied, and unwanted files are not included in the results.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Handle Git Worktrees
&lt;/h3&gt;

&lt;p&gt;For repositories using Git worktrees, &lt;code&gt;.git&lt;/code&gt; is a file reference rather than a directory. Repomix adjusts the ignore patterns accordingly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gitPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.git&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isWorktree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;isGitWorktreeRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gitPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isWorktree&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gitIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;adjustedIgnorePatterns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.git/**&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gitIndex&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;adjustedIgnorePatterns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gitIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;adjustedIgnorePatterns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.git&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ignore the reference file&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents &lt;code&gt;.git&lt;/code&gt; reference files from being mistakenly included in the search.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Organization
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;.gitignore&lt;/code&gt; feature in Repomix is implemented in a &lt;strong&gt;modular and organized way&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;fileSearch.ts&lt;/code&gt;&lt;/strong&gt; → Contains the main logic for searching files and directories while respecting ignore rules.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;defaultIgnore.js&lt;/code&gt;&lt;/strong&gt; → Provides default ignore patterns like &lt;code&gt;node_modules/**&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;errorHandle.ts&lt;/code&gt;&lt;/strong&gt; → Defines custom errors such as &lt;code&gt;RepomixError&lt;/code&gt; and &lt;code&gt;PermissionError&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;permissionCheck.ts&lt;/code&gt;&lt;/strong&gt; → Checks read/write permissions for directories.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Helper functions&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;parseIgnoreContent&lt;/code&gt; → Reads &lt;code&gt;.git/info/exclude&lt;/code&gt; and converts it into usable ignore patterns.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;escapeGlobPattern&lt;/code&gt; → Escapes special characters in paths to prevent glob errors.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isGitWorktreeRef&lt;/code&gt; → Detects if &lt;code&gt;.git&lt;/code&gt; is a Git worktree reference file.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This separation makes it &lt;strong&gt;easy to extend or modify behavior&lt;/strong&gt;, such as adding new ignore patterns or supporting additional ignore files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things I Learned
&lt;/h2&gt;

&lt;p&gt;Reading this code taught me several important lessons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Practical use of glob patterns&lt;/strong&gt; – Using &lt;code&gt;globby&lt;/code&gt; with ignore patterns allows Repomix to filter files effectively, respecting &lt;code&gt;.git/info/exclude&lt;/code&gt;, optional &lt;code&gt;.gitignore&lt;/code&gt;, and other custom rules.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error handling strategy&lt;/strong&gt; – File operations are wrapped in &lt;code&gt;try/catch&lt;/code&gt; blocks and errors are re-thrown as &lt;strong&gt;custom error classes&lt;/strong&gt;, which is cleaner than letting Node.js errors propagate.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Git-specific handling&lt;/strong&gt; – Git worktrees change how &lt;code&gt;.git&lt;/code&gt; appears (as a reference file instead of a directory). Repomix handles this gracefully to avoid false positives.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Combining multiple ignore sources&lt;/strong&gt; – &lt;code&gt;.git/info/exclude&lt;/code&gt;, optional &lt;code&gt;.gitignore&lt;/code&gt;, default ignore patterns, custom patterns, and explicit output files are &lt;strong&gt;all merged&lt;/strong&gt; carefully to ensure accurate file filtering.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenges &amp;amp; Unknowns
&lt;/h2&gt;

&lt;p&gt;Initially, locating which folders need to be looked into for reading the code was difficult.&lt;/p&gt;

&lt;p&gt;Some parts of the code were tricky:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understanding &lt;strong&gt;Git worktree handling&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Figuring out how &lt;code&gt;.git/info/exclude&lt;/code&gt; interacts with &lt;code&gt;.gitignore&lt;/code&gt; patterns.
&lt;/li&gt;
&lt;li&gt;Behavior with &lt;strong&gt;nested &lt;code&gt;.gitignore&lt;/code&gt; files&lt;/strong&gt; isn’t fully clear since the code primarily reads &lt;code&gt;.git/info/exclude&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Strategies for Reading the Code
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Started with using &lt;code&gt;git grep&lt;/code&gt; to find all references to &lt;code&gt;.gitignore&lt;/code&gt; and &lt;code&gt;globby&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;Looked into the &lt;code&gt;searchFiles&lt;/code&gt; function to understand the file search flow.
&lt;/li&gt;
&lt;li&gt;Traced &lt;strong&gt;helper functions&lt;/strong&gt; like &lt;code&gt;getIgnorePatterns&lt;/code&gt;, &lt;code&gt;parseIgnoreContent&lt;/code&gt;, &lt;code&gt;escapeGlobPattern&lt;/code&gt;, and &lt;code&gt;isGitWorktreeRef&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Used AI (ChatGPT) to clarify tricky logic, especially around Git worktrees.
&lt;/li&gt;
&lt;li&gt;Checked the &lt;strong&gt;configuration interface&lt;/strong&gt; &lt;code&gt;RepomixConfigMerged&lt;/code&gt; to see how users can enable &lt;code&gt;.gitignore&lt;/code&gt; support.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think using &lt;code&gt;git grep&lt;/code&gt; for locating the files that need to be read and then &lt;code&gt;ChatGPT&lt;/code&gt; for understanding the code worked for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Repomix’s &lt;code&gt;.gitignore&lt;/code&gt; support is a &lt;strong&gt;reliable and efficient file filtering system&lt;/strong&gt;. It combines &lt;code&gt;.git/info/exclude&lt;/code&gt;, optional &lt;code&gt;.gitignore&lt;/code&gt;, default patterns, and custom rules, while handling Git worktrees correctly. This ensures only relevant files are included, making it a strong example of Git-aware file handling in Node.js.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>opensource</category>
      <category>learning</category>
    </item>
    <item>
      <title>Adding .gitignore Support to share-my-repo: A New Feature to Streamline Repository Analysis</title>
      <dc:creator>Hitesh Sachdeva</dc:creator>
      <pubDate>Wed, 29 Oct 2025 04:35:37 +0000</pubDate>
      <link>https://forem.com/hsachdeva9/adding-gitignore-support-to-share-my-repo-a-new-feature-to-streamline-repository-analysis-3f95</link>
      <guid>https://forem.com/hsachdeva9/adding-gitignore-support-to-share-my-repo-a-new-feature-to-streamline-repository-analysis-3f95</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This feature was inspired by &lt;a href="https://repomix.com" rel="noopener noreferrer"&gt;Repomix&lt;/a&gt;, a repository analysis tool that allows users to examine their codebases with various built-in features, including file exclusion based on &lt;code&gt;.gitignore&lt;/code&gt; files. Repomix demonstrated how powerful this feature could be in streamlining repository analysis, and I wanted to bring similar functionality to &lt;code&gt;share-my-repo&lt;/code&gt;. By supporting &lt;code&gt;.gitignore&lt;/code&gt;, users of &lt;code&gt;share-my-repo&lt;/code&gt; can now avoid unnecessary files from their analysis, just as Repomix does.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is the &lt;code&gt;.gitignore&lt;/code&gt; Support Feature?
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;.gitignore&lt;/code&gt; support in &lt;a href="https://github.com/hsachdeva9/share-my-repo" rel="noopener noreferrer"&gt;share-my-repo&lt;/a&gt; is designed to automatically skip files that are ignored by Git during repository analysis. When processing a repository, &lt;code&gt;share-my-repo&lt;/code&gt; will now check for the &lt;code&gt;.gitignore&lt;/code&gt; file and exclude any files or directories listed there from being included in the output. This is particularly useful when working with large repositories that contain irrelevant or non-code files that don't need to be analyzed.&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Detecting &lt;code&gt;.gitignore&lt;/code&gt;:&lt;/strong&gt;
The tool checks for &lt;code&gt;.gitignore&lt;/code&gt; files at the root and any subdirectories of the repository.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reading the Patterns:&lt;/strong&gt;
It reads the patterns inside &lt;code&gt;.gitignore&lt;/code&gt; and matches them against the files and directories in the repository to exclude them from the analysis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Excluding Ignored Files:&lt;/strong&gt;
Files and directories that match the &lt;code&gt;.gitignore&lt;/code&gt; patterns are automatically excluded from the output, ensuring that the repository analysis focuses only on relevant code files.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What Problem Does It Solve?
&lt;/h3&gt;

&lt;p&gt;When analyzing large repositories, developers often face the issue of unnecessary files (such as log files, build artifacts, or temporary files) cluttering the analysis output. By supporting &lt;code&gt;.gitignore&lt;/code&gt;, this feature ensures that only relevant files are processed, making repository analysis faster, more focused, and more efficient. It saves developers time and allows them to focus on what truly matters,  the code. &lt;a href="https://github.com/hsachdeva9/share-my-repo/issues/10" rel="noopener noreferrer"&gt;issue-10&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What I Did Similarly to Repomix
&lt;/h3&gt;

&lt;p&gt;The main aspect I took from Repomix was respecting &lt;code&gt;.gitignore&lt;/code&gt; rules automatically.&lt;/p&gt;

&lt;p&gt;Previously, my tool would process all files in a repository, including files that are meant to be ignored. Now, it follows a two-step approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Detect Ignored Files First:&lt;/strong&gt; The script reads &lt;code&gt;.gitignore&lt;/code&gt; in the repository root (and optionally in subdirectories) and parses the patterns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Filter Files:&lt;/strong&gt; During file discovery, any files matching the &lt;code&gt;.gitignore&lt;/code&gt; patterns are automatically excluded before further processing.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is similar to how &lt;a href="https://repomix.com" rel="noopener noreferrer"&gt;Repomix&lt;/a&gt; avoids processing ignored files to keep the dataset clean and relevant.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I Did Differently
&lt;/h3&gt;

&lt;p&gt;The main difference lies in the flexibility and integration with other features. Repomix automatically respects &lt;code&gt;.gitignore&lt;/code&gt; but does not provide additional user-level configuration.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;share-my-repo&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users can still provide &lt;strong&gt;include/exclude patterns&lt;/strong&gt; on top of &lt;code&gt;.gitignore&lt;/code&gt; rules.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.gitignore&lt;/code&gt; support is integrated with &lt;strong&gt;TOML configuration&lt;/strong&gt;, so users can enable or disable it by default.&lt;/li&gt;
&lt;li&gt;The filtering works alongside other features like &lt;strong&gt;file previews, line numbers, and file size limits&lt;/strong&gt;, which Repomix does not provide in the same context.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next Steps for the Feature
&lt;/h2&gt;

&lt;p&gt;While the &lt;code&gt;.gitignore&lt;/code&gt; support feature is functional, there are still some areas where it can be improved and expanded. Here are the next steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Support for Multiple &lt;code&gt;.gitignore&lt;/code&gt; Files:&lt;/strong&gt; Currently, the tool only checks the &lt;code&gt;.gitignore&lt;/code&gt; file in the root directory. Adding support for multiple &lt;code&gt;.gitignore&lt;/code&gt; files in subdirectories will further improve the tool's flexibility. &lt;a href="https://github.com/yourusername/share-my-repo/issues/11" rel="noopener noreferrer"&gt;Support multiple .gitignore files&lt;/a&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CLI Flag to Toggle &lt;code&gt;.gitignore&lt;/code&gt; Support:&lt;/strong&gt; Adding a flag to enable or disable &lt;code&gt;.gitignore&lt;/code&gt; processing from the command line would give users more control over their analysis. &lt;a href="https://github.com/yourusername/share-my-repo/issues/12" rel="noopener noreferrer"&gt;Add CLI toggle for .gitignore&lt;/a&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Error Handling Improvements:&lt;/strong&gt; Ensuring that the tool handles malformed &lt;code&gt;.gitignore&lt;/code&gt; files gracefully will make the tool more robust.&lt;a href="https://github.com/yourusername/share-my-repo/issues/13" rel="noopener noreferrer"&gt;Handle malformed .gitignore files gracefully&lt;/a&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>analytics</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
