<?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: Patricia Parrocha</title>
    <description>The latest articles on Forem by Patricia Parrocha (@patriciaann).</description>
    <link>https://forem.com/patriciaann</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%2F997570%2F0a7464a7-1705-48bb-9e98-8fa593822ec4.png</url>
      <title>Forem: Patricia Parrocha</title>
      <link>https://forem.com/patriciaann</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/patriciaann"/>
    <language>en</language>
    <item>
      <title>Jenkins to Github Action Workflows Migration: Applying the DRY principle</title>
      <dc:creator>Patricia Parrocha</dc:creator>
      <pubDate>Wed, 07 Aug 2024 04:12:17 +0000</pubDate>
      <link>https://forem.com/devsatasurion/jenkins-to-github-action-workflows-migration-applying-the-dry-principle-292l</link>
      <guid>https://forem.com/devsatasurion/jenkins-to-github-action-workflows-migration-applying-the-dry-principle-292l</guid>
      <description>&lt;p&gt;&lt;em&gt;Basic knowledge on Jenkins and Github Actions is required.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Jenkins has been my DevOps team's CI/CD tool of choice for years, however we recently had to say goodbye and switch to GitHub Action Workflows. During the planning stage of our migration, we discovered a lot of redundancy in the Jenkins pipelines, so we devised a strategy to address this by utilizing the DRY concept.&lt;/p&gt;

&lt;p&gt;This blog post explains the application of the "Don't Repeat Yourself" (DRY) software development principle, which helps us minimize the effort needed for the migration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;DRY&lt;/em&gt;&lt;/strong&gt; is a key concept in programming that emphasizes the importance of reducing redundancy. Duplicating code is not just ugly it is bad practice because it would make your code hard to maintain. If you need to fix one part on your code, you have to do it on all other parts where it is repeated and often times that can cause issues whenever the fix is not done properly.&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%2Fz21kvfchsfca2jtzn8dz.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%2Fz21kvfchsfca2jtzn8dz.png" alt="Example Jenkins Pipelines"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping it DRY
&lt;/h2&gt;

&lt;p&gt;Before writing any GitHub workflow, we took a closer look first into our Jenkins pipelines to identify areas we could apply the DRY principle. &lt;/p&gt;

&lt;h3&gt;
  
  
  Duplicate Stages Across Multiple Pipelines
&lt;/h3&gt;

&lt;p&gt;Here you can see an example of two separate Jenkins pipelines for deployment that accept the same parameter and have very similar stages. &lt;/p&gt;

&lt;h4&gt;
  
  
  Pipeline A: DB Deployment
&lt;/h4&gt;

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

&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;

    &lt;span class="n"&gt;parameters&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
     &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s1"&gt;'JIRA_TICKET'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;description:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;defaultValue:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;stages&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Get JIRA Ticket Details'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
                  &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Fetch Ticket Details from JIRA API'&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

       &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB Deployment'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
                   &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Logging in to DB'&lt;/span&gt;
                   &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Running Deployment Script'&lt;/span&gt;
                   &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Parsing logs for any ORA errors'&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

       &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'JIRA Update'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
                   &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Update JIRA Ticket with Deployment Result'&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; 
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;h4&gt;
  
  
  Pipeline B: App Deployment
&lt;/h4&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;

&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;

    &lt;span class="n"&gt;parameters&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
     &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s1"&gt;'JIRA_TICKET'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;description:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;defaultValue:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;stages&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Get JIRA Ticket Details'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Fetch Ticket Details from JIRA API'&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'App Deployment'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
                  &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Logging in to Server'&lt;/span&gt;
                  &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Running Deployment Script'&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; 
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Trigger Post Deploy and HealthChecks'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
                  &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Running post deploy and healthchecks'&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; 
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'JIRA Update'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
                   &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Update JIRA Ticket with Deployment Result'&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; 
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


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

&lt;/div&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%2Fzy6knwpk2ug01ri6pg86.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%2Fzy6knwpk2ug01ri6pg86.png" alt="Looks Familiar"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first and last stages, Stage &lt;code&gt;Get JIRA Ticket Details&lt;/code&gt; and Stage &lt;code&gt;JIRA Update&lt;/code&gt;, are similar and are quite repeated in other pipelines since we have a dependency on getting JIRA ticket field values to identify what to deploy and also keep track of deployment status.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pipelines triggered frequently from other pipelines
&lt;/h3&gt;

&lt;p&gt;We also identified which pipelines are triggered from other pipelines frequently, which are usually parameterized pipelines. Taking the above's example, we also have other pipelines that should trigger the same set of steps in &lt;code&gt;Pipeline A&lt;/code&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%2Fipnrowdqmn35er0jehf4.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%2Fipnrowdqmn35er0jehf4.jpg" alt="Jobs triggered from other pipelines"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;To avoid duplication when we migrate to Github Workflows, below was our solution:&lt;/p&gt;

&lt;p&gt;For similar stages across pipelines →  Use Composite Actions&lt;/p&gt;

&lt;p&gt;For pipelines triggered frequently from other pipelines →  Use Reusable Workflows&lt;/p&gt;

&lt;p&gt;In our case, we choose these solutions, which Github already offers, to achieve our goals below:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simplified Migration&lt;/strong&gt;&lt;br&gt;
Migrating existing Jenkins pipelines often involves translating Jenkinsfile scripts into Github Action Workflows. Composite actions and reusable workflows help manage complexity by isolating and reusing common logic &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reusability&lt;/strong&gt;&lt;br&gt;
Mirrors Jenkins's shared libraries or reusable pipelines, easing the transition. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Maintainability&lt;/strong&gt; &lt;br&gt;
Using shared logic in one place simplifies maintenance and  troubleshooting. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Using Composite Actions&lt;/strong&gt; ✨
&lt;/h2&gt;

&lt;p&gt;Composite actions allow you to turn a step or set of steps into an action and reuse it on multiple GitHub workflows. In Jenkins, this is similar to a Jenkins Shared Library.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example Composite Action
&lt;/h3&gt;

&lt;p&gt;Here is an example composite action created for the &lt;code&gt;Get JIRA Ticket Details&lt;/code&gt; stage above. &lt;/p&gt;

&lt;p&gt;This composite action handles getting information for a build given a JIRA Ticket number and importing the information fetched from an API as environment variables which can be used multiple times across other workflows.&lt;/p&gt;

&lt;p&gt;On &lt;code&gt;composite-repo&lt;/code&gt; repository, under a folder called &lt;code&gt;get-jira&lt;/code&gt; an &lt;code&gt;action.yaml&lt;/code&gt; like below is created:&lt;/p&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Get&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;JIRA&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Ticket&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Details'&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Fetch&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;JIRA&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Ticket&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;details'&lt;/span&gt;
&lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;jira_ticket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;JIRA Ticket&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;jira_user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;JIRA Username&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;jira_pass&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;JIRA Password&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;runs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;using&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;composite'&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;Get JIRA Ticket Details&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;get_jira&lt;/span&gt;
      &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&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;curl -u ${{ inputs.jira_user }}:${{ inputs.jira_pass }} \&lt;/span&gt;
       &lt;span class="s"&gt;"$jira_api/${{ inputs.jira_ticket }}" &amp;gt; jira.json&lt;/span&gt;

       &lt;span class="s"&gt;summary=$(cat jira.json | jq -r '.fields.summary')&lt;/span&gt;
       &lt;span class="s"&gt;status=$(cat jira.json | jq -r '.fields.status')&lt;/span&gt;
       &lt;span class="s"&gt;artifacts=$(cat jira.json | jq -r '.fields.artifact')&lt;/span&gt;

       &lt;span class="s"&gt;echo "SUMMARY=$summary" &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;
       &lt;span class="s"&gt;echo "STATUS=$status" &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;
       &lt;span class="s"&gt;echo "ARTIFACTS=$artifacts" &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Here’s how the composite action above is used on a workflow. Note that &lt;code&gt;get-jira&lt;/code&gt; is the name of the action and &lt;code&gt;v1&lt;/code&gt; is the branch name or tag on &lt;code&gt;composite-repo&lt;/code&gt; repository.&lt;/p&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;Deployment Pipeline&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;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;JIRA_TICKET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;JIRA Ticket number&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&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;Deploy&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;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;Get JIRA Ticket Fields&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;composite-repo/get-jira@v1&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;jira_ticket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ inputs.JIRA_TICKET }}&lt;/span&gt;
        &lt;span class="na"&gt;jira_user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.JIRA_USER }}&lt;/span&gt;
        &lt;span class="na"&gt;jira_pass&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.JIRA_PASS }}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Now that we have this composite action, we can add and make changes to this and all other workflows which uses this are automatically updated. &lt;/p&gt;

&lt;p&gt;We also made other actions we can reuse aside from this one and  and we ended up having like a tool box full of tools we can just use on any of our workflows. 🔧 🔨&lt;/p&gt;

&lt;h3&gt;
  
  
  Lessons Learned when creating Composite Actions 🎉
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A mono repo containing commonly used composite actions with the below structure can be created.&lt;/li&gt;
&lt;/ul&gt;

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

  |-action-1
  |  |-action.yaml
  |-action-2
  |  |-action.yaml



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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use branches or tags to version the composite actions for easy maintenance and testing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Avoid combining very different sets of steps into the same composite action, as it will become complicated to use and may require multiple optional parameters and conditions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Document actions for easy use. Tools like &lt;code&gt;npalm/action-docs&lt;/code&gt; CLI can be used to automate the creation of documentation.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Using Reusable Workflows ✨
&lt;/h2&gt;

&lt;p&gt;For Jenkins pipelines triggered frequently from other pipelines, we created reusable workflows. &lt;/p&gt;

&lt;p&gt;Unlike Composite actions which can be run on steps, reusable workflows are run directly as jobs and can even use the matrix strategy option to trigger it multiple times. &lt;/p&gt;

&lt;p&gt;In our case we have one main workflow which triggers multiple DB Deployment workflows for each JIRA ticket and environment, so we created a reusable workflow for that purpose. &lt;/p&gt;

&lt;p&gt;Example Reusable Workflow: &lt;code&gt;db_deploy.yaml&lt;/code&gt; &lt;/p&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;DB Deployment&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_call&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
         &lt;span class="na"&gt;JIRA_TICKET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
           &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Jira Ticket&lt;/span&gt;
           &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
           &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
         &lt;span class="na"&gt;ENVIRONMENT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
           &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Environment&lt;/span&gt;
           &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
           &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&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;Oracle-Deploy&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;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&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="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;Get JIRA Ticket Fields&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;composite-repo/get-jira@v1&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;jira_ticket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ inputs.JIRA_TICKET }}&lt;/span&gt;
          &lt;span class="na"&gt;jira_user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.JIRA_USER }}&lt;/span&gt;
          &lt;span class="na"&gt;jira_pass&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.JIRA_PASS }}&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;Get DB Details&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;get-db&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;composite-repo/get-db-details@v1&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;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ inputs.ENVIRONMENT }}&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;DB Deployment&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;composite-repo/db-deploy@v1&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;dbhost&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.get-db.outputs.dbhost }}&lt;/span&gt;
          &lt;span class="na"&gt;dbport&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.get-db.outputs.dbport }}&lt;/span&gt;
          &lt;span class="na"&gt;dbuser&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DB_USER }}&lt;/span&gt;
          &lt;span class="na"&gt;dbpass&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DB_PASS }}&lt;/span&gt;
          &lt;span class="na"&gt;dbscripts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.ARTIFACTS }}&lt;/span&gt;



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

&lt;/div&gt;

&lt;p&gt;Here’s how the reusable workflow above is used on a workflow. Since we have multiple deployments for multiple JIRA tickets, a reusable workflow is used on a job with matrix strategy.&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="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
  &lt;span class="na"&gt;Oracle_Deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;br&gt;
       &lt;span class="na"&gt;jira&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ fromJSON(needs.jobname.outputs.tickets) }}&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;workflows-repo/.github/workflows/db_deploy.yaml@v1&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;JIRA_TICKET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.jira.ticket }}&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;ENVIRONMENT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.jira.environment }}&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Lessons Learned when creating Reusable Workflows: 🎉&lt;br&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Reusable workflows and composite actions are quite similar. Use reusable workflows to reuse an entire workflow with multiple jobs and steps, and use composite actions for small tasks that can be reused in a step.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Too much reuse can lead to complications. Package related steps and jobs only into one reusable workflow and, for other purposes, create a separate one.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reusing of workflows is allowed up to 4 levels only. If your main workflow does not need to wait for the triggered reusable workflow to complete, you can use the github CLI &lt;code&gt;gh workflow run &amp;lt;workflow name&amp;gt;&lt;/code&gt; on a step to trigger it using the  &lt;code&gt;workflow_dispatch&lt;/code&gt; event instead.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Composite Actions and Reusable Workflows can be used to avoid duplication and ease the migration from Jenkins to Github Actions. &lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
