<?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: Riddhi Jathar</title>
    <description>The latest articles on DEV Community by Riddhi Jathar (@riddhi_jathar_0b47391235b).</description>
    <link>https://dev.to/riddhi_jathar_0b47391235b</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3740418%2Fa7e81672-1b7b-40e4-9e77-2bd1338821c2.png</url>
      <title>DEV Community: Riddhi Jathar</title>
      <link>https://dev.to/riddhi_jathar_0b47391235b</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/riddhi_jathar_0b47391235b"/>
    <language>en</language>
    <item>
      <title>Fast-Forward-Only Merges in GitHub Actions: A Safer Way to Sync Branches</title>
      <dc:creator>Riddhi Jathar</dc:creator>
      <pubDate>Tue, 07 Jul 2026 13:35:16 +0000</pubDate>
      <link>https://dev.to/riddhi_jathar_0b47391235b/fast-forward-only-merges-in-github-actions-a-safer-way-to-sync-branches-4j58</link>
      <guid>https://dev.to/riddhi_jathar_0b47391235b/fast-forward-only-merges-in-github-actions-a-safer-way-to-sync-branches-4j58</guid>
      <description>&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;We maintain a repo with two long-lived branches, master and main, that need to stay in sync. The manual routine was: checkout locally, pull both, merge, hope nothing conflicts, push. Every time. Repetitive, and one distracted moment away from a messy merge commit or a conflict resolution you didn't mean to make.&lt;br&gt;
A co-maintainer on this repo set up a GitHub Action to handle it instead. I just liked it enough to write about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The workflow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Manual Fast-Forward Master to Main&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;merge-branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&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;Checkout Code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&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;Configure Git Identity&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;git config user.name "github-actions[bot]"&lt;/span&gt;
          &lt;span class="s"&gt;git config user.email "41898282+github-actions[bot]@users.noreply.github.com"&lt;/span&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;Attempt Fast-Forward Merge&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;git_merge&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;git fetch origin main&lt;/span&gt;
          &lt;span class="s"&gt;git fetch origin master&lt;/span&gt;

          &lt;span class="s"&gt;git checkout main&lt;/span&gt;

          &lt;span class="s"&gt;if ! git merge origin/master --ff-only; then&lt;/span&gt;
            &lt;span class="s"&gt;echo "::error::Cannot fast-forward! Histories have diverged or conflicts exist. Aborting."&lt;/span&gt;
            &lt;span class="s"&gt;exit 1&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&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;Push Changes&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;git push origin main&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;workflow_dispatch  means this only runs when someone manually clicks "Run workflow" in the Actions tab, no accidental triggers on push or schedule. Click it, and the job:&lt;/p&gt;

&lt;p&gt;Checks out the full history (fetch-depth: 0 needed so the merge actually has both branches' history to compare)&lt;br&gt;
Checks out main&lt;br&gt;
Tries git merge origin/master --ff-only&lt;br&gt;
Pushes if it worked&lt;/p&gt;

&lt;p&gt;Why --ff-only is the part worth understanding&lt;br&gt;
A regular git merge will happily create a merge commit, or ask you to resolve conflicts, or in some configs auto-resolve things in ways you didn't explicitly review. --ff-only refuses all of that. It only allows the merge if main hasn't diverged from master at all, meaning main can just "fast-forward" its pointer forward to match, with no new commit and no ambiguity about what changed.&lt;br&gt;
If the branches have diverged, the flag makes git fail instead of guessing. That's the whole safety net. The workflow catches that failure explicitly (if ! git merge ... ; then echo "::error::..."; exit 1; fi) so it shows up loud and clear in the Actions log instead of silently pushing something wrong.&lt;br&gt;
That's the actual lesson here: automating a merge is easy. Automating it so that it fails safely instead of doing something unintended, that's the part worth copying into your own workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is this novel?
&lt;/h2&gt;

&lt;p&gt;No, and worth being upfront about that. workflow_dispatch has existed since GitHub introduced it in 2020, and fast-forward-only merges are just core git behavior. This isn't a new invention, it's a small, well-known pattern for anyone who's automated branch syncing or release promotion with Actions.&lt;br&gt;
But it's a good example of a broader habit worth building: find the repetitive manual git step you don't fully trust yourself to do carefully every single time, and hand it to a machine that does the exact same safe thing, consistently, with a paper trail.&lt;/p&gt;

</description>
      <category>githubactions</category>
      <category>github</category>
      <category>devops</category>
      <category>automation</category>
    </item>
    <item>
      <title>Jenkins Freestyle Jobs vs Pipeline: Which Should You Use?</title>
      <dc:creator>Riddhi Jathar</dc:creator>
      <pubDate>Thu, 29 Jan 2026 20:15:32 +0000</pubDate>
      <link>https://dev.to/riddhi_jathar_0b47391235b/jenkins-freestyle-jobs-vs-pipeline-which-should-you-use-5eao</link>
      <guid>https://dev.to/riddhi_jathar_0b47391235b/jenkins-freestyle-jobs-vs-pipeline-which-should-you-use-5eao</guid>
      <description>&lt;p&gt;When working with Jenkins, you'll encounter two main job types. Here's what I learned about when to use each.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Freestyle Jobs: GUI-Based Configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Freestyle projects are Jenkins traditional approach, where everything is configured through the web interface.&lt;br&gt;
It is best for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Simple build tasks&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Quick prototypes and experiments&lt;/li&gt;
&lt;li&gt;Jobs that rarely change&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Limitations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Configuration isn't version-controlled&lt;/li&gt;
&lt;li&gt;Manual replication across projects&lt;/li&gt;
&lt;li&gt;Gets messy with complex workflows&lt;/li&gt;
&lt;li&gt;No code review for changes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Pipeline Jobs: Code-Driven Automation&lt;/strong&gt;&lt;br&gt;
Pipeline defines your CI/CD workflow as code in a Jenkinsfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pipeline {
    agent any {
    stages {
        stage('Build') {
           steps {
               sh 'npm install &amp;amp;&amp;amp; npm run build'
           }
        }
        stage('Test') {
           steps {
               sh 'npm test'
           }
        }
        stage('Deploy') {
            steps {
               sh './deploy.sh'
           }
        }
     }
}

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

&lt;/div&gt;



&lt;p&gt;It is best for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Production deployments&lt;/li&gt;
&lt;li&gt;Multi-stage workflows (dev-&amp;gt; stage -&amp;gt; prod)&lt;/li&gt;
&lt;li&gt;Anything you need to replicate&lt;/li&gt;
&lt;li&gt;Complex orchestration needs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Key advantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Version controlled with your code&lt;/li&gt;
&lt;li&gt;Reusable across projects ( just copy the Jenkinsfile )&lt;/li&gt;
&lt;li&gt;Code review for pipeline changes&lt;/li&gt;
&lt;li&gt;Supports parallel execution&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Bottom&amp;nbsp;Line&lt;/strong&gt;&lt;br&gt;
Freestyle jobs are beginner-friendly and work fine for simple tasks. Pipelines are the modern standard for production CI/CD because they're scalable, version-controlled, and automatable.&lt;br&gt;
Start learning with freestyle to understand Jenkins basics, then transition to pipelines for anything production-grade. You don't need to convert everything; keep simple jobs as freestyle if they work well.&lt;/p&gt;




&lt;p&gt;Further reading:&amp;nbsp;&lt;br&gt;
&lt;a href="https://www.jenkins.io/doc/book/pipeline/" rel="noopener noreferrer"&gt;Jenkins Pipeline Documentation&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.jenkins.io/doc/book/pipeline/pipeline-best-practices/" rel="noopener noreferrer"&gt;Pipeline Best Practices&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
