<?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>DEV Community: Sebastian Rath</title>
    <description>The latest articles on DEV Community by Sebastian Rath (@sebastianrath).</description>
    <link>https://dev.to/sebastianrath</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%2F595829%2F998c5f0e-02ca-4658-a1dc-f75d1eea4e1c.jpeg</url>
      <title>DEV Community: Sebastian Rath</title>
      <link>https://dev.to/sebastianrath</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sebastianrath"/>
    <language>en</language>
    <item>
      <title>🐙 ✅ GitHub Actions workflows: How to replace YAML with a visual editor</title>
      <dc:creator>Sebastian Rath</dc:creator>
      <pubDate>Wed, 29 Nov 2023 23:36:15 +0000</pubDate>
      <link>https://dev.to/sebastianrath/github-actions-workflows-how-to-replace-yaml-with-a-visual-editor-36dd</link>
      <guid>https://dev.to/sebastianrath/github-actions-workflows-how-to-replace-yaml-with-a-visual-editor-36dd</guid>
      <description>&lt;p&gt;Hey folks! 👋&lt;/p&gt;

&lt;p&gt;In this blog post I would like to talk about GitHub Actions workflows and how I believe they could become much better. Over the last few years I have worked with many repos and I noted how much time I spent on creating and modifying YAML files for GitHub workflows, rather than working on the actual project.&lt;/p&gt;

&lt;p&gt;So in true developer fashion, I am working on a solution called &lt;a href="https://www.actionforge.dev" rel="noopener noreferrer"&gt;Actionforge&lt;/a&gt;. Below is a short dive into what I am aiming to solve and how.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Actionforge?
&lt;/h2&gt;

&lt;p&gt;Using Actionforge, you can visually create and modify your workflow within a user-friendly editor in VS Code. Each node of your graph represents a GitHub Action. Together, they form what I call an 'Action Graph'. Check out an example &lt;a href="https://www.actionforge.dev/github/actionforge/playground/main/.github/workflows/graphs/build-and-publish.yml" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6eu3c23qvtza34mv7qu.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6eu3c23qvtza34mv7qu.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbfxmrxnaifajkoqjy8bz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbfxmrxnaifajkoqjy8bz.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once your action graph is ready, you can commit it to your repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to execute an Action Graph as a GitHub workflow?
&lt;/h2&gt;

&lt;p&gt;To execute an action graph as your workflow, you simply create a simple GitHub workflow that includes a single step with the actionforge/action. Then you point to the Action Graph file you want to run. After this setup, you can edit your graph from the Actionforge UI.&lt;/p&gt;

&lt;p&gt;Here is a simple example to execute an Action Graph:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="c1"&gt;# Beginning of your workflow.yml&lt;/span&gt;&lt;br&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
  &lt;span class="na"&gt;run-graph&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and deploy my app&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actionforge/&lt;a href="mailto:action@0.45.3"&gt;action@0.45.3&lt;/a&gt;&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;br&gt;
      &lt;span class="na"&gt;graph_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./github/workflows/graphs/my-graph.yml&lt;/span&gt;&lt;br&gt;
&lt;span class="c1"&gt;# End&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  The Linear Limitation&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Traditional GitHub workflows follow a linear execution order from top to bottom, which can be restrictive. In contrast, Actionforge's Action Graphs enable more complex and versatile node setups with different flow like if-conditions, for-loops, switches and more.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4rudyh1fu28cq1jk57rz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4rudyh1fu28cq1jk57rz.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Executing Action Graphs directly on your GH Runners
&lt;/h2&gt;

&lt;p&gt;Actionforge does not depend on external cloud services. &lt;code&gt;actionforge/action&lt;/code&gt; serves as an interpreter, converting your visually designed graph into a sequence of GitHub Action nodes that execute as usual on your designated GitHub runners.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi200cp603ywyhbnn6agw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi200cp603ywyhbnn6agw.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All repositories will be made public in December. I would love to hear your story. Share your GitHub Actions workflow challenges. Check out &lt;a href="https://www.actionforge.dev" rel="noopener noreferrer"&gt;Actionforge&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks for reading ❤️&lt;/p&gt;

</description>
      <category>githubactions</category>
      <category>github</category>
      <category>cicd</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>A deep dive into file locking with NodeJS</title>
      <dc:creator>Sebastian Rath</dc:creator>
      <pubDate>Mon, 07 Jun 2021 14:21:15 +0000</pubDate>
      <link>https://dev.to/sebastianrath/today-i-released-a-version-control-software-for-2d-3d-artists-and-graphic-designers-made-in-angular-electron-nck</link>
      <guid>https://dev.to/sebastianrath/today-i-released-a-version-control-software-for-2d-3d-artists-and-graphic-designers-made-in-angular-electron-nck</guid>
      <description>&lt;p&gt;I developed a version control software for graphic designers and 2D/3D artists called &lt;a href="https://www.snowtrack.io" rel="noopener noreferrer"&gt;Snowtrack&lt;/a&gt; in Angular and Electron. In this blog post, I will cover some technical challenges about file locks which I faced during the development of Snowtrack.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Snowtrack?
&lt;/h2&gt;

&lt;p&gt;Snowtrack is an intuitive, easy-to-use, and super-fast version control software for graphic projects. Its purpose is to make version control accessible to graphic designers and 2D/3D artists with a non-technical workflow.&lt;/p&gt;

&lt;p&gt;To get a better understanding of Snowtracks user interface check out the following screenshot:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvq9d2lnwf9ahuy719vfo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvq9d2lnwf9ahuy719vfo.png" alt="snowtrack-screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I used to build it
&lt;/h2&gt;

&lt;p&gt;For the UI application I used a combination of Angular and Electron. The underlying version control engine is called &lt;strong&gt;SnowFS&lt;/strong&gt;, an open-source project I developed as a fast and simple alternative to &lt;em&gt;Git&lt;/em&gt; and &lt;em&gt;Git LFS&lt;/em&gt;. Feel free to check it out on &lt;a href="https://github.com/snowtrack/snowfs" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. A few months ago I wrote a blog post about it &lt;a href="https://dev.to/sebmtl/snowfs-let-s-bring-version-control-to-graphic-projects-10p8"&gt;here&lt;/a&gt; on &lt;em&gt;dev.to&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical challenge no. 1
&lt;/h2&gt;

&lt;p&gt;Graphic projects can differ in size tremendously. From a single Photoshop file up to a 50 GB file set of 3D scenes, textures, and assets. These project types come with their own set of problems. In the following I want to clear up some misconceptions about the topic around &lt;em&gt;file locking&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  File Locking
&lt;/h2&gt;

&lt;p&gt;Take a look at the code snippet below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// Process 1
fd = fs.openSync("~/foo", "w");

// Process 2
fd = fs.openSync("~/foo", "w");


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

&lt;/div&gt;

&lt;p&gt;Imagine more than one process wants to open the same file at the same time. What do you think will happen?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; It depends on the OS and if you're the maintainer of all processes.&lt;/p&gt;

&lt;p&gt;When you call &lt;code&gt;fs.openSync&lt;/code&gt; NodeJS will forward the call behind the scenes to an OS function as you can see from &lt;a href="https://github.com/libuv/libuv/blob/b201c1a0f0b1ba2365dc285f466ff6fe5307decf/src/unix/fs.c#L380-L389" rel="noopener noreferrer"&gt;this C code&lt;/a&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;static&lt;/span&gt; &lt;span class="kt"&gt;ssize_t&lt;/span&gt; &lt;span class="nf"&gt;uv__fs_open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uv_fs_t&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;req&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="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;-&amp;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;req&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;O_CLOEXEC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mode&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 function &lt;code&gt;open(..)&lt;/code&gt; is an OS function and available in all operating systems. But the internals of this function differ between Windows, Linux and macOS so I will cover them separately.&lt;/p&gt;

&lt;h1&gt;
  
  
  macOS/Linux
&lt;/h1&gt;

&lt;p&gt;Technically, neither macOS nor Linux have true file-locking mechanisms. Although you can read or write-lock a file using a function called &lt;a href="https://man7.org/linux/man-pages/man2/fcntl.2.html" rel="noopener noreferrer"&gt;&lt;code&gt;fcntl&lt;/code&gt;&lt;/a&gt;, only programs which use this function regard and respect the file lock. This means, any other process which &lt;strong&gt;doesn't&lt;/strong&gt; use &lt;code&gt;fcntl&lt;/code&gt; and directly wants to open a file can acquire a file handle and manipulate the content as long as the file permissions allow it. What a bummer.&lt;/p&gt;

&lt;p&gt;That's why file locking on macOS and Linux is also called &lt;a href="https://news.ycombinator.com/item?id=17601581" rel="noopener noreferrer"&gt;"advisory file locking"&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Windows
&lt;/h2&gt;

&lt;p&gt;Windows is more complicated in that matter. Windows offers two functions to open a file. Either through the Windows API function called &lt;a href="https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea" rel="noopener noreferrer"&gt;CreateFile&lt;/a&gt; (yes, that's really the name to open files),...&lt;/p&gt;

&lt;p&gt;...or through &lt;a href="https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/open?view=msvc-160" rel="noopener noreferrer"&gt;&lt;code&gt;open(..)&lt;/code&gt;&lt;/a&gt;. But the &lt;code&gt;open(..)&lt;/code&gt; function on Windows is a POSIX extension and uses &lt;code&gt;CreateFile&lt;/code&gt; internally as well.&lt;/p&gt;

&lt;p&gt;As we've seen above NodeJS uses &lt;code&gt;open(..)&lt;/code&gt;, but since we know that this is just a wrapper for &lt;code&gt;CreateFile&lt;/code&gt;, let's check out that function:&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;// The low-level open function of Windows.&lt;/span&gt;
&lt;span class="n"&gt;HANDLE&lt;/span&gt; &lt;span class="nf"&gt;CreateFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;LPCSTR&lt;/span&gt;                &lt;span class="n"&gt;lpFileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;DWORD&lt;/span&gt;                 &lt;span class="n"&gt;dwDesiredAccess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;DWORD&lt;/span&gt;                 &lt;span class="n"&gt;dwShareMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;LPSECURITY_ATTRIBUTES&lt;/span&gt; &lt;span class="n"&gt;lpSecurityAttributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;DWORD&lt;/span&gt;                 &lt;span class="n"&gt;dwCreationDisposition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;DWORD&lt;/span&gt;                 &lt;span class="n"&gt;dwFlagsAndAttributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;HANDLE&lt;/span&gt;                &lt;span class="n"&gt;hTemplateFile&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;CreateFile&lt;/code&gt; has a parameter called &lt;code&gt;dwShareMode&lt;/code&gt;. A file that is opened with &lt;code&gt;dwShareMode=0&lt;/code&gt; &lt;strong&gt;cannot&lt;/strong&gt; be opened again until its handle has been closed.&lt;/p&gt;

&lt;p&gt;So if you use &lt;code&gt;open(..)&lt;/code&gt; on a file that was already open by another process with &lt;code&gt;CreateFile(…, dwShareMode=0)&lt;/code&gt; you receive this error message:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The process cannot access the file because it is being used by another process&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On the other hand, if you use &lt;code&gt;fs.openSync&lt;/code&gt; in NodeJS, or &lt;code&gt;open(..)&lt;/code&gt; in C/C++, to open a file that hasn't been opened yet, you cannot prevent another application from modifying it*.&lt;/p&gt;

&lt;p&gt;* Unless you you use file permissions as a workaround, but that’s not really a file lock.&lt;/p&gt;

&lt;p&gt;To prove this, you will see that our &lt;code&gt;fs.openSync&lt;/code&gt; call executes &lt;code&gt;CreateFile&lt;/code&gt; with the read/write shared flags to comply with the POSIX standard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0ghmvit3mr3azndpgur.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0ghmvit3mr3azndpgur.png" alt="foo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This means on Windows you cannot prevent another application from opening and modifying your file if you don't use &lt;code&gt;CreateFile&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does this have to do with Snowtrack?
&lt;/h2&gt;

&lt;p&gt;Imagine a user saving a big file in a graphic application and while the file is still being written to disk, the user attempts to commit the file change. How does Snowtrack deal with this?&lt;/p&gt;

&lt;p&gt;As we learned, &lt;code&gt;open(..)&lt;/code&gt; has no file locking and most applications don't even follow the file protocol and Snowtrack cannot control how Photoshop, Blender, and co. open and write their files.&lt;/p&gt;

&lt;p&gt;This means the only reliable chance of detecting if a file is still being written by another process is to check prior to a commit if any process on the system has a write handle on that file.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;On Windows, I solved this with a custom helper process and and the Windows API of &lt;a href="https://docs.microsoft.com/en-us/windows/win32/rstmgr/about-restart-manager" rel="noopener noreferrer"&gt;Restart Manager&lt;/a&gt; which is mainly used for installers to ensure the files it is about to replace are not open anymore.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On MacOS I invoke the system process &lt;a href="https://ss64.com/osx/lsof.html" rel="noopener noreferrer"&gt;&lt;code&gt;/usr/sbin/lsof&lt;/code&gt;&lt;/a&gt; (list open files) with an inclusion of the working-directory to speed up the execution of this command.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What else?
&lt;/h3&gt;

&lt;p&gt;The development of Snowtrack came with countless technical challenges and I would be happy to share more insights. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;File locking&lt;/em&gt;, &lt;em&gt;Electron/Angular&lt;/em&gt; &lt;em&gt;race conditions&lt;/em&gt;, &lt;em&gt;I/O saturation&lt;/em&gt;, &lt;em&gt;build server&lt;/em&gt;, &lt;em&gt;update mechanisms&lt;/em&gt;, &lt;em&gt;edge cases&lt;/em&gt;, .. with this project I touched many subjects and I would be happy to write a follow-up blog post if you are interested. Let me know in the comments below.&lt;/p&gt;

&lt;p&gt;If you want to support SnowFS, Snowtrack or me then feel free to join me on &lt;a href="https://twitter.com/snowtrack_io" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks for reading :-)&lt;/p&gt;

&lt;h2&gt;
  
  
  TLDR
&lt;/h2&gt;

&lt;p&gt;Don't get me started on file-locking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Addendum: What about the &lt;em&gt;"File In Use"&lt;/em&gt; dialog in Windows?
&lt;/h2&gt;

&lt;p&gt;If you are a Windows user you might have seen this error message before:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fah632ge0flhg77dgd4pf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fah632ge0flhg77dgd4pf.png" alt="Untitled"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Windows, or rather NTFS, behaves very different compared to other file systems like &lt;em&gt;HFS+&lt;/em&gt;, &lt;em&gt;APFS&lt;/em&gt;, &lt;em&gt;ext3&lt;/em&gt;, ...&lt;/p&gt;

&lt;p&gt;There is no equivalent to inodes in NTFS and therefore no garbage collection deletes the file if the last file handle to an already deleted file is closed. The &lt;em&gt;File in Use&lt;/em&gt; dialog only indicates, that if any process has a file handle to a given file (no matter how it got opened), it cannot be renamed, moved, or deleted. That does not imply a file lock on the file content.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>angular</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>SnowFS ❄️ Let's bring version control to graphic projects</title>
      <dc:creator>Sebastian Rath</dc:creator>
      <pubDate>Sun, 14 Mar 2021 19:32:03 +0000</pubDate>
      <link>https://dev.to/sebastianrath/snowfs-let-s-bring-version-control-to-graphic-projects-10p8</link>
      <guid>https://dev.to/sebastianrath/snowfs-let-s-bring-version-control-to-graphic-projects-10p8</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fsnowtrack%2Fsnowfs%2Fraw%2Fmain%2Fimg%2Fterminal.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fsnowtrack%2Fsnowfs%2Fraw%2Fmain%2Fimg%2Fterminal.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Welcome to my first article here on &lt;strong&gt;Dev.to&lt;/strong&gt;. In this article, I will be sharing an overview of an open-source project I kicked off to explore ideas and solutions on how to bring version control to graphic projects.&lt;/p&gt;

&lt;p&gt;The open-source project is called &lt;a href="https://www.github.com/snowtrack/snowfs" rel="noopener noreferrer"&gt;SnowFS&lt;/a&gt; and it is a lightweight command-line application and library for binary files with a focus on graphic files.&lt;/p&gt;

&lt;h2&gt;
  
  
  But why?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/lYKvaJ8EQTzCU/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/lYKvaJ8EQTzCU/giphy.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I recently started developing an &lt;a href="https://www.snowtrack.io" rel="noopener noreferrer"&gt;UI application&lt;/a&gt; that brings local version control and artists/designers together. Given the effort of the Git community to support larger files, I tried Git+LFS for my first prototype. It seemed, after several successful tests I've overcome the inner demons that were screaming &lt;strong&gt;Don't even try to use Git with such big files&lt;/strong&gt;. Means Git with LFS is a nice solution to tackle such needs.&lt;/p&gt;

&lt;p&gt;Unfortunately, the devil is in the detail, and some features beneficial for the software development process can be quite counterintuitive for other project types. For example, the hash integrity of Git is great to ensure the integrity of a software repository, but not really needed for a graphic project. For such project types, the requirement to delete individual files or removing entire commits without rewriting the history all the time is more important.&lt;/p&gt;

&lt;p&gt;Basically, I needed Git, just more lightweight, support for big files, and a fraction of the functionality. And that's how &lt;strong&gt;SnowFS&lt;/strong&gt; was born. It supports...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;...deleting of entire commits or individual files&lt;/li&gt;
&lt;li&gt;...instant snapshots&lt;/li&gt;
&lt;li&gt;...instant rollback&lt;/li&gt;
&lt;li&gt;...super-fast-detection of large modified files&lt;/li&gt;
&lt;li&gt;...block-cloning and copy-on-write supports for APFS, ReFS, and Btrfs&lt;/li&gt;
&lt;li&gt;...and more.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How does SnowFS perform?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/XJePrgQp0Szr5Nwi8l/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/XJePrgQp0Szr5Nwi8l/giphy.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Below is a comparison between Git and SnowFS on a Macbook Pro (2020) with an APFS formatted SSD to check-in, delete and restore a 4GB Photoshop File.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git lfs track *.psd
$ git add texture.psd   # 20164ms
$ snow add texture.psd  # 4596ms &amp;lt;-- Not bad
$ git rm texture.psd    # 575ms
$ snow rm texture.psd   # 111ms &amp;lt;-- Also ok
$ git checkout HEAD~1   # 9739ms
$ snow checkout HEAD~1  # 1ms &amp;lt;-- Yeah!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Where to go from here?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.github.com/snowtrack/snowfs" rel="noopener noreferrer"&gt;SnowFS&lt;/a&gt;&lt;/strong&gt; is a great lightweight and flexible project to explore and experiment with new ideas for version control. If you are interested in the project, we are looking for your expertise to share and learn!&lt;/p&gt;

&lt;p&gt;In that sense, &lt;strong&gt;Happy versioning&lt;/strong&gt;!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
