<?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: Kathryn DiPippo</title>
    <description>The latest articles on DEV Community by Kathryn DiPippo (@kdipippo).</description>
    <link>https://dev.to/kdipippo</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%2F346572%2F16748901-15d0-4389-b85b-37d184bc6edf.png</url>
      <title>DEV Community: Kathryn DiPippo</title>
      <link>https://dev.to/kdipippo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kdipippo"/>
    <language>en</language>
    <item>
      <title>The Essential Sprint Ceremonies</title>
      <dc:creator>Kathryn DiPippo</dc:creator>
      <pubDate>Mon, 07 Jul 2025 21:48:12 +0000</pubDate>
      <link>https://dev.to/kdipippo/the-essential-sprint-ceremonies-4co9</link>
      <guid>https://dev.to/kdipippo/the-essential-sprint-ceremonies-4co9</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Background&lt;/li&gt;
&lt;li&gt;
Daily Stand-up

&lt;ul&gt;
&lt;li&gt;Daily Stand-up: When&lt;/li&gt;
&lt;li&gt;Daily Stand-up: How&lt;/li&gt;
&lt;li&gt;Daily Stand-up: Why&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Triage / Sizing Sessions

&lt;ul&gt;
&lt;li&gt;Triage / Sizing Sessions: When&lt;/li&gt;
&lt;li&gt;Triage / Sizing Sessions: How&lt;/li&gt;
&lt;li&gt;Triage / Sizing Sessions: Why&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Backlog Grooming

&lt;ul&gt;
&lt;li&gt;Backlog Grooming: When&lt;/li&gt;
&lt;li&gt;Backlog Grooming: How&lt;/li&gt;
&lt;li&gt;Backlog Grooming: Why&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Sprint Retro

&lt;ul&gt;
&lt;li&gt;Sprint Retro: When&lt;/li&gt;
&lt;li&gt;Sprint Retro: How&lt;/li&gt;
&lt;li&gt;Sprint Retro: Why&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Sprint Planning

&lt;ul&gt;
&lt;li&gt;Sprint Planning: When&lt;/li&gt;
&lt;li&gt;Sprint Planning: How&lt;/li&gt;
&lt;li&gt;Sprint Planning: Why&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;There are many other guides out there about Sprint Ceremonies. I am taking the time in this article to explain my perspective on the usefulness of each meeting and how to ensure that the time spent to coordinate on them is useful and not a distraction or a waste of time.&lt;/p&gt;

&lt;p&gt;For each sprint ceremony, I've broken down into subsections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;When&lt;/strong&gt; to have the meetings and for how long&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How&lt;/strong&gt; I have learned to effectively run the meeting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why&lt;/strong&gt; each meeting is necessary&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Daily Stand-up
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Daily Stand-up: When
&lt;/h3&gt;

&lt;p&gt;Every day blocked off for 30 minutes but could take as long as 10 minutes even on large development teams.&lt;/p&gt;

&lt;h3&gt;
  
  
  Daily Stand-up: How
&lt;/h3&gt;

&lt;p&gt;The most effective stand-ups I've been a part of made use of an Excel spreadsheet to allow all team members to write their answers before the start of the actual meeting. In the spreadsheet, dedicate the first column to the list of team member names in the order they will present (I am used to alphabetical order by first name). Title the next 3 columns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What I did yesterday&lt;/li&gt;
&lt;li&gt;What I will do today&lt;/li&gt;
&lt;li&gt;Any blockers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Team members will fill out their rows either before or at the very start of the meeting time. Then, in order from top-to-bottom, members will recite their entries out loud. Any further discussion topics that don't belong in the previous rows will be marked at the bottom of the sheet as a "post-scrum".&lt;/p&gt;

&lt;p&gt;Once every team member has recited their row and all post-scrums have been discussed, the meeting ends.&lt;/p&gt;

&lt;h3&gt;
  
  
  Daily Stand-up: Why
&lt;/h3&gt;

&lt;p&gt;I personally enjoy the "what I did yesterday" and "what I will do today" entries as personal goals / checklists to work toward and to keep me focused. But as a team lead, the "blockers" column is the most important, followed by post-scrums. Nothing is more demoralizing than being blocked by a task needed to be performed by someone on the same team and waiting weeks at a time. A blocker that is a quick action item that is visibly written every day proves to be a solid reminder.&lt;/p&gt;

&lt;p&gt;Stand-up also provides a dedicated venue as well for brief but constant team discussion. For teams with remote employees, there's a significant mental difference between reaching out for a Zoom call vs. leaning over into a coworker's desk to ask a question.&lt;/p&gt;

&lt;p&gt;It's important to ensure that the stand-up meeting stays capped to 30 minutes and any further discussion is properly scheduled and scoped to the correct number of people. This isn't to avoid having long, team conversations but to instead ensure that the meeting itself does not subtly expand to too long.&lt;/p&gt;

&lt;p&gt;As new team members acclimate to the company and its processes, it may happen that most stand-ups take place with no blockers or post-scrums to review. This is fine and good! Stand-ups should still be slotted to 30 minutes in the event that there is a larger discussion that needs to happen, even if you'll now experience regular 5-10 minute meetings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Triage / Sizing Sessions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Triage / Sizing Sessions: When
&lt;/h3&gt;

&lt;p&gt;Triage should be held at a regular cadence depending on how often new tickets come in. For some teams, this may need to be a dedicated block of time after every stand-up; for others, ideally once-a-week. I believe 30 minutes is enough, but adjust longer or shorter as needed based on intake.&lt;/p&gt;

&lt;p&gt;Sizing Sessions are separately scheduled ad-hoc meetings when a larger development project is split out into smaller tickets. Because these Epics will typically entail much larger wholistic discussions, I recommend booking a full hour and ending early if deemed appropriate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Triage / Sizing Sessions: How
&lt;/h3&gt;

&lt;p&gt;Triage tickets are reviewed in order starting from oldest-to-newest. Sizing session tickets are reviewed in the appropriate order to work on them within the given project.&lt;/p&gt;

&lt;p&gt;Everyone on the team will be participating in Scrum Poker as they review and size each ticket. I am partial to &lt;a href="https://playscrummy.com/" rel="noopener noreferrer"&gt;playscrummy.com&lt;/a&gt; as the tool to facilitate this.&lt;/p&gt;

&lt;p&gt;For each ticket, each team member needs to mentally answer the following questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the ticket appropriately categorized (i.e. correct labels and priority)?&lt;/li&gt;
&lt;li&gt;Are the outcomes for this ticket clearly understood?&lt;/li&gt;
&lt;li&gt;Are the outstanding questions required of this ticket small? For example, the outstanding questions are related to actual code implementation details ("What unit-tests do I write?") and not issues with what conceptually is being asked ("Why are we doing this change when only 1 person has asked for it? Have we checked with other stakeholders on what their thoughts on this change are?")&lt;/li&gt;
&lt;li&gt;Would I be able to address this ticket if it were assigned to me? If not, what other information do I need in the ticket to provide further clarity?&lt;/li&gt;
&lt;li&gt;Is the scope of this ticket appropriate? Are follow-up tickets needed in order to break out what is being requested into smaller steps?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice, whoever is leading the session will reveal the ticket, read the description aloud, and ask for general feedback. If there is feedback, the lead may document these questions as comments onto the ticket. If there is significant feedback, they may then move onto the next ticket without moving the current ticket out of the inbound queue. This means the ticket will be re-reviewed at the next Triage / Sizing Session.&lt;/p&gt;

&lt;p&gt;If there is no feedback, the team then uses scrum poker to assign the number of story points to the task. There are a number of guides out there for what story points should correlate to; the important part is that the concept of story points remains consistent to the team. For me, the typical vibe for each number is as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1: Very quick, almost asinine. For example:

&lt;ul&gt;
&lt;li&gt;A typo is reported on a documentation page and needs to be changed.&lt;/li&gt;
&lt;li&gt;A configuration update is needed that involves toggling 4 buttons after following a procedure doc.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;2: Small change. For example:

&lt;ul&gt;
&lt;li&gt;A code change, like a quick bug fix, is needed that requires no test updates&lt;/li&gt;
&lt;li&gt;Boosting code coverage by adding more unit-tests without a functional code change&lt;/li&gt;
&lt;li&gt;Writing documentation&lt;/li&gt;
&lt;li&gt;A code change that would be big but is a copy-and-paste of existing code with smaller changes added onto it&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;3: Regular change. Majority of tickets fall into this bucket. For example:

&lt;ul&gt;
&lt;li&gt;Code changes with both an update to the base code along with adding unit-tests&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;5: Large change. For example:

&lt;ul&gt;
&lt;li&gt;Any change where the path-to-action is uncertain, like investigating a new library or new architectural strategy&lt;/li&gt;
&lt;li&gt;A code change that requires a refactor that touches a significant part of the codebase&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;8: Too big! Anything given this size typically means that the ticket needs to be re-conceptualized. What uncertainty is causing this to be too big? Are there clear "sections" of outcomes that would be best to split out?

&lt;ul&gt;
&lt;li&gt;An exception would be if the work itself is appropriate to group together, i.e. performing a small configuration change against 50+ repositories. Keeping an 8-story-point ticket would be considered more user-friendly to update than 50 1-point tickets and maybe more accurate to the amount of work performed.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;After all members have voted, reveal the results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If all numbers are the same (i.e. "3, 3, 3"), assign that number ("3") and move to the next ticket.&lt;/li&gt;
&lt;li&gt;If all numbers are within 1 deviation of each other (i.e. "3, 5, 3"), assign the highest number ("5") and move to the next ticket.&lt;/li&gt;
&lt;li&gt;If all numbers are 2+ deviations of each other (i.e. "2, 5, 3"), the members of the team who voted the lowest ("2") and highest ("5") need to be encouraged to discuss their reasoning. This means that there is a fundamental disagreement over the expectations of the ticket. Having this conversation now means that an incorrect assumption was successfully avoided in the future where it would be more difficult to course-correct. By the end of the discussion, members will re-vote to decide the final story point value and move to the next ticket.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Process repeats until all tickets are reviewed or the meeting time has reached its end.&lt;/p&gt;

&lt;h3&gt;
  
  
  Triage / Sizing Sessions: Why
&lt;/h3&gt;

&lt;p&gt;Every ticket submitted to the team gets to be reviewed as a team. This enables knowledge sharing among team members and avoids having one person bear the burden of understanding and breaking down work. For larger teams or for teams with remote workers, this enables also having a shared understanding of what members of the team are working on.&lt;/p&gt;

&lt;p&gt;The key needs for story points are two-fold:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enable discussion between team members in cases where team members understood the request in a ticket differently&lt;/li&gt;
&lt;li&gt;Better understand the speed ("velocity") of a software development team to best estimate project deadlines.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Having each ticket sized at the same time will also surface conversations that may have been skipped. For example, a team reviews a new ticket: verbally, everyone says they understand the contents of the ticket and what it set out to accomplish. However, after sizing the ticket, one of the team members scores it a 2 and another scores it an 8. That's a huge difference! Had the team not sized the ticket, there may have been unspoken confusions over what the actual request of the ticket contains. Keep in mind a delicate balance is needed for how long this session should take. I have seen sizing sessions going on for an extended period of time that result in all team members consistently choosing safe "3"s in the interest of wrapping up sooner without back-and-forth.&lt;/p&gt;

&lt;p&gt;From the developers' perspectives, story points should not matter. They should not be used as a comparison metric between team members, and software development team leads should avoid setting up a display for points accomplished as a scoreboard. For other teams like those in IT, a scoreboard may be a fun challenge if the tasks are all very procedural. For software development, it's highly likely that 8 tickets that are each "1" story point will take a significantly shorter amount of time than 1 ticket with "8" story points. This may incentivize wanting to avoid larger, more difficult 5-8 point initiatives in favor of scooping up smaller tickets immediately as they come in, or the potential where manual processes that require smaller tickets to be created are pushed forward instead of seeking out automation.&lt;/p&gt;

&lt;p&gt;Meanwhile, team leads should look at the end of each sprint to examine the number of story points accomplished and seek out a trend. This trending line - referred to as "velocity" - will provide a rough estimate for what a comfortable range of story points that can be taken on and completed for each sprint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Backlog Grooming
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Backlog Grooming: When
&lt;/h3&gt;

&lt;p&gt;Once a week for an hour at a point in the week where there's a lull - i.e. afternoon on Fridays - set aside 30 minutes - 1 hour for going through the oldest tickets in your ticket backlog.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backlog Grooming: How
&lt;/h3&gt;

&lt;p&gt;Bookmark a query or filtered view of tasks that sorts all of them by age created. As a team, review tickets in the same manner as Triage / Sizing Sessions. For each ticket, answer the following questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is the request in the ticket still relevant? Is it outdated or a duplicate of a ticket? If a duplicate, decided as a team which of the two tickets to rely on as the primary ticket (could be this older one or the newer one, depending on how thorough and relevant the content is).&lt;/li&gt;
&lt;li&gt;Is the request still needed? If the ticket is to address an issue that popped up once two years ago, this request should be treated as stale and opened as a new ticket if it resurfaces.&lt;/li&gt;
&lt;li&gt;Is the ticket's original submitter still asking for this request? Reach back out to them to confirm if their request has changed in the time that the ticket has been sitting in the backlog or if there is a more urgent need that they would rather have addressed.&lt;/li&gt;
&lt;li&gt;Re-size the ticket as a group to update the value and confirm that the team as a whole have a consistent understanding of the original request.&lt;/li&gt;
&lt;li&gt;Re-prioritize the ticket if appropriate to take on in the near future.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The vibe of this meeting is chill and nostalgic. If the team has been around long enough, this may be a fun trip down memory lane to 3-year old tickets.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backlog Grooming: Why
&lt;/h3&gt;

&lt;p&gt;It's easy if left unchecked for a software development ticket backlog to build up. There's may nice-to-haves for improving processes but only a limited window for what tasks can actually be prioritized. This meeting enables a balance to be had: all team members should feel comfortable documenting tasks they would like to have accomplished for the betterment of the team and the company, but the team does not feel overwhelmed by having a giant never-ending backlog of tasks that may not be prioritized.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sprint Retro
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Sprint Retro: When
&lt;/h3&gt;

&lt;p&gt;Sprint Retrospective and Sprint Planning go hand-in-hand. Every 2 weeks, schedule a 1-hour meeting with the first half as the sprint retro and the second half as the sprint planning (see Sprint Planning: When).&lt;/p&gt;

&lt;h3&gt;
  
  
  Sprint Retro: How
&lt;/h3&gt;

&lt;p&gt;Prior to the meeting, set up a team document with the following 4 headings:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;🎖️ Kudos - &lt;em&gt;specific thanks to team members who stood out with their efforts over the past sprint&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;🟢 What went well?&lt;/li&gt;
&lt;li&gt;🔴 What went wrong?&lt;/li&gt;
&lt;li&gt;💡 Action Items&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Set up a 5 minute timer for the group to go through and write answers to the first 3 questions, leaving "Action Items" untouched for now. An alternative as well is for the team lead to go through each question in order and ask for members to speak up, where the manager then transcribes the answers. I am still on the fence about either approach. I would recommend the "Kudos" answers be said out loud; it's more meaningful personally when I was a junior developer to hear a senior developer say that my efforts were appreciated.&lt;/p&gt;

&lt;p&gt;After the timer - or if following the manager-lead approach - go through the answers to each column and encourage any follow-up discussions. The answers to questions 2 and 3 should not be specific towards any individual; we are a team, so discussions should ensure ways that the team can better work together to address any shortcomings. If frustrations are pointed towards a different team in the company, what are ways we can improve our own processes to better assist them? (i.e. developing specific procedures to assist with investigation issues on our end before handing off externally for others to troubleshoot).&lt;/p&gt;

&lt;p&gt;While going through the discussion for questions 2 and 3, any follow-ups are now written down in the "Action Items" column. After the meeting, the individual Action Items should be delegated to team members to re-made as proper software development tickets. Some retrospectives may not result in Action Items if the result of the discussion was either that there was no glaring process improvement that needs to be made or an issue that was encountered has a low probability of resurfacing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sprint Retro: Why
&lt;/h3&gt;

&lt;p&gt;Software is written and maintained by people, so encouraging self-reflection is a must to resolve both software development issues and people issues. Team members should not feel that the issues with the team are stuck in their ways. Everyone should feel comfortable also clearly expressing frustrations in a blameless manner. Individualize the gains and socialize the losses. Clear, open, and honest communication.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sprint Planning
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Sprint Planning: When
&lt;/h3&gt;

&lt;p&gt;Sprint planning takes place right after Sprint retrospective or in the same day. Schedule a 30 minute to 1 hour block of time right after or later in the day, depending on how much preparation is needed before the meeting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sprint Planning: How
&lt;/h3&gt;

&lt;p&gt;Look over the past several sprints to see a trend of the number of story points are being closed out. Use this metric ("velocity") as a guide for how many story points to add onto the workload. If there are outstanding tickets not completed from the prior sprint, take them into account when setting up new work to take on. Communicate with stakeholders about potential delays in timing if those outstanding tickets will impact the original estimated timeline for the initiative.&lt;/p&gt;

&lt;p&gt;As a team lead, look over the delegated story points just to get a sense for each member's workload and not as a comparison. If someone has a noticeable carryover from sprint-to-sprint not being accomplished, see if some of their tickets can be delegated to others (or in your regular 1-on-1, bring up with them to invite the discussion).&lt;/p&gt;

&lt;p&gt;Ideally, for a given team of people, one member can be assigned to an "Operations" ticket queue for one-off requests, short repeated manual procedures, and monitoring application health and logs. The remaining members of the team would be assigned to larger project initiatives with clear estimated deadlines split out.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sprint Planning: Why
&lt;/h3&gt;

&lt;p&gt;Once a team is a well-oiled machine, Sprint Planning becomes a manner of communicating a timeline for completing a project with stakeholders with proper scheduling well in advance. A new project request comes in from a key stakeholder; how does this request compare to other priorities in the business? After breaking the project down to its individual steps in a sizing session, the cumulative story points compared to the team's velocity will inform us how many sprints to dedicate to accomplishing the work.&lt;/p&gt;

&lt;p&gt;The end result is a clear queue of work and estimates. The team is happy because deadlines are not a matter of setting everything to as-soon-as-possible and should be comfortable to accomplish and push for (avoiding crunch, where possible and where the company does not enable crunch-conditions). Stakeholders are happy because they have a clear estimate for when they will be unblocked or can expect work to be delivered without it being lost to a void of uncertainty.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Visualizing local git repository statuses with MermaidJS and a Python CLI</title>
      <dc:creator>Kathryn DiPippo</dc:creator>
      <pubDate>Sat, 05 Jul 2025 04:26:05 +0000</pubDate>
      <link>https://dev.to/kdipippo/visualizing-local-git-repository-statuses-with-mermaidjs-and-a-python-cli-4o91</link>
      <guid>https://dev.to/kdipippo/visualizing-local-git-repository-statuses-with-mermaidjs-and-a-python-cli-4o91</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Background&lt;/li&gt;
&lt;li&gt;How to Use&lt;/li&gt;
&lt;li&gt;Example Outputs&lt;/li&gt;
&lt;li&gt;Python Script&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;If you're like me, you have a folder with all of your &lt;code&gt;git&lt;/code&gt; repositories held in the same location. In a more organized vision of myself, I switch between repos and work on tasks one-at-a-time. I start work in one &lt;code&gt;git&lt;/code&gt; repository, get it finished, push it into a PR, and then I switch to another repo. After I release the PR, I go back into my local folder and switch back to &lt;code&gt;main&lt;/code&gt; and pull in latest changes.&lt;/p&gt;

&lt;p&gt;Instead though, I have often ended up with a ton of outstanding work in all of my different folders. I will start work only to be sidelined by more important issues and fires. By the time I return, enough time has passed that I've lost track of what the outstanding work needs to be continued. I'll make a separate folder "TEMP" and &lt;code&gt;git clone&lt;/code&gt; a fresh version of the project, work in there, accidentally abandon that, keep making separate folders... until finally, my &lt;code&gt;git&lt;/code&gt; repository folders are a mess of duplicates and work-in-progresses.&lt;/p&gt;

&lt;p&gt;In the past to remedy this, I have manually typed out the folder structure and documented what branch I was looking at and the state of outstanding work and slowly whittle the folders back down to my ideal state. But what if I made a script that automatically assembled a &lt;a href="https://mermaid.js.org/" rel="noopener noreferrer"&gt;&lt;code&gt;Mermaid&lt;/code&gt;&lt;/a&gt; diagram instead?&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use
&lt;/h2&gt;

&lt;p&gt;The provided Python script (compatible with both MacOS/Linux and Windows operating systems) at the bottom of this article contains a single command powered by the &lt;a href="https://click.palletsprojects.com/en/stable/" rel="noopener noreferrer"&gt;click&lt;/a&gt; library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python script.py &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Usage: script.py [OPTIONS]

  CLI command to return a mermaid diagram of all git repositories - including
  in subdirectories in a specified local folder along with the branch they're
  currently pointed to and their number of uncommitted changes.

Options:
  --root-dir TEXT  Relative or absolute path to directory to traverse.
                   Defaults to treating current dir where the script is being
                   run as the root.
  --only-errors    If True, hide any git repositories that are pointed to the
                   base branch and have no outstanding changes.
  --output TEXT    If provided, save Mermaid string to file with filename.
  --name TEXT      If provided, filter results to the provided git repository
                   name.
  --help           Show this message and exit.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, this command will take the directory located at &lt;code&gt;../..&lt;/code&gt; from where the script is being called, walk through all of its directories and subdirectories, and generate a &lt;a href="https://mermaid.js.org/" rel="noopener noreferrer"&gt;&lt;code&gt;Mermaid&lt;/code&gt;&lt;/a&gt; file &lt;code&gt;mermaid.mmd&lt;/code&gt; that shows the complete tree structure and the status of all of the &lt;code&gt;git&lt;/code&gt; repositories found.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python script.py &lt;span class="nt"&gt;--root-dir&lt;/span&gt; ../.. &lt;span class="nt"&gt;--output&lt;/span&gt; mermaid.mmd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The additional option &lt;code&gt;--only-errors&lt;/code&gt; will hide any &lt;code&gt;git&lt;/code&gt; repositories already at a "blank slate" status. This will enable me to know what folders I have yet to address.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python script.py &lt;span class="nt"&gt;--root-dir&lt;/span&gt; ../.. &lt;span class="nt"&gt;--output&lt;/span&gt; mermaid.mmd &lt;span class="nt"&gt;--only-errors&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, the additional option &lt;code&gt;--name&lt;/code&gt; will filter the &lt;code&gt;git&lt;/code&gt; repositories to the name specified. This is useful for cases where the same &lt;code&gt;git&lt;/code&gt; repository is cloned in multiple places and to generate diagram with an easy comparison.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example Outputs
&lt;/h2&gt;

&lt;p&gt;This is an example diagram returned from &lt;code&gt;python script.py --root-dir ../.. --output mermaid.mmd&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fslhydi4syiwoqmzp34lh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fslhydi4syiwoqmzp34lh.png" alt="Example script.py output"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
classDef HasChangesPointedToBase fill:#f0ad4e
classDef HasChangesNotPointedToBase fill:#d9534f
classDef NoChangesNotPointedToBase fill:#5bc0de

path["path"] --&amp;gt; path_to
path_to["to"] --&amp;gt; path_to_git-repo-1
path_to["to"] --&amp;gt; path_to_git-repo-2
path_to["to"] --&amp;gt; path_to_TEMP
path_to_TEMP["TEMP"] --&amp;gt; path_to_TEMP_git-repo-1
path_to["to"] --&amp;gt; path_to_TEMP2
path_to_TEMP2["TEMP2"] --&amp;gt; path_to_TEMP2_git-repo-2

path_to_git-repo-1["&amp;lt;b&amp;gt;git-repo-1&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;FEATURE-1&amp;lt;br&amp;gt;All changes committed"]:::NoChangesNotPointedToBase
path_to_git-repo-2["&amp;lt;b&amp;gt;git-repo-2&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;main&amp;lt;br&amp;gt;9 files changed, 70 insertions(+), 25 deletions(-)"]:::HasChangesPointedToBase
path_to_TEMP_git-repo-1["&amp;lt;b&amp;gt;git-repo-1&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;FEATURE-1&amp;lt;br&amp;gt;20 files changed, 150 insertions(+), 125 deletions(-)"]:::HasChangesNotPointedToBase
path_to_TEMP2_git-repo-2["&amp;lt;b&amp;gt;git-repo-2&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;main&amp;lt;br&amp;gt;All changes committed"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an example diagram returned from &lt;code&gt;python script.py --root-dir ../.. --output mermaid.mmd --only-errors&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F3u3mdlevitagmg1wnwly.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F3u3mdlevitagmg1wnwly.png" alt="Example script.py output with --only-errors"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
classDef HasChangesPointedToBase fill:#f0ad4e
classDef HasChangesNotPointedToBase fill:#d9534f
classDef NoChangesNotPointedToBase fill:#5bc0de

path["path"] --&amp;gt; path_to
path_to["to"] --&amp;gt; path_to_git-repo-1
path_to["to"] --&amp;gt; path_to_git-repo-2
path_to["to"] --&amp;gt; path_to_TEMP
path_to_TEMP["TEMP"] --&amp;gt; path_to_TEMP_git-repo-1

path_to_git-repo-1["&amp;lt;b&amp;gt;git-repo-1&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;FEATURE-1&amp;lt;br&amp;gt;All changes committed"]:::NoChangesNotPointedToBase
path_to_git-repo-2["&amp;lt;b&amp;gt;git-repo-2&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;main&amp;lt;br&amp;gt;9 files changed, 70 insertions(+), 25 deletions(-)"]:::HasChangesPointedToBase
path_to_TEMP_git-repo-1["&amp;lt;b&amp;gt;git-repo-1&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;FEATURE-1&amp;lt;br&amp;gt;20 files changed, 150 insertions(+), 125 deletions(-)"]:::HasChangesNotPointedToBase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Python Script
&lt;/h2&gt;

&lt;p&gt;To run this script, the following Python modules need to be installed beforehand (either in a &lt;code&gt;venv&lt;/code&gt; or globally):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="py"&gt;click&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;=8.1.7&lt;/span&gt;
&lt;span class="py"&gt;Jinja2&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;=3.1.3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;CLI command to return a mermaid diagram of all git repositories - including in subdirectories in a specified local
folder along with the branch they&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;re currently pointed to and their number of uncommitted changes.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;platform&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;shutil&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;stat&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;click&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;jinja2&lt;/span&gt;

&lt;span class="n"&gt;MERMAID_FULL_TEMPLATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
flowchart LR
classDef HasChangesPointedToBase fill:#f0ad4e
classDef HasChangesNotPointedToBase fill:#d9534f
classDef NoChangesNotPointedToBase fill:#5bc0de
{% for mermaid_route in mermaid_routes %}
{{ mermaid_route }}
{% endfor %}
{% for mermaid_element in mermaid_elements %}
{{ mermaid_element }}
{% endfor %}
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="n"&gt;MERMAID_ELEMENT_TEMPLATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
{{ element_id }}
[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;
&amp;lt;b&amp;gt;{{ git_repo_name }}&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;
{{ git_branch }}&amp;lt;br&amp;gt;
{% if git_outstanding_changes == &lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="s"&gt; %}
All changes committed
{% else %}
{{ git_outstanding_changes }}
{% endif %}
&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]
{% if has_changes and is_pointed_to_base_branch %}
:::HasChangesPointedToBase
{% elif has_changes and not is_pointed_to_base_branch %}
:::HasChangesNotPointedToBase
{% elif not has_changes and not is_pointed_to_base_branch %}
:::NoChangesNotPointedToBase
{% endif %}
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_handle_readonly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nb"&gt;object&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="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;On Windows, .idx files in the .git/objects/pack folder get set to readonly and throw PermissionError.
    They need to be converted to write before deletion.
    https://stackoverflow.com/questions/21778356/python-shutil-rmtree-cannot-remove-git-dir-on-win7

    Args:
        function (Callable[..., Any]): Function which raised the exception, depends on the platform and implementation.
        path (str): Path name passed to Function.
        excinfo (BaseException): Exception that was raised. Exceptions raised by onexc will not be caught.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chmod&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;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;S_IWRITE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;function&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;def&lt;/span&gt; &lt;span class="nf"&gt;delete_file_or_folder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_or_folder_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Delete file or folder at relative path. Pythonic version of &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rm -rf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.

    Args:
        file_or_folder_name (str): Relative path to file or folder to recursively delete.

    Raises:
        LookupError: Raised if input fails matching on isfile() or isdir().
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# Silence exception if path does not exit.
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;os&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="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_or_folder_name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&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="nf"&gt;isfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_or_folder_name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_or_folder_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&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="nf"&gt;isdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_or_folder_name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;system&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Windows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Try chmod-ing files if error occurs while running on Windows.
&lt;/span&gt;            &lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rmtree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_or_folder_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onexc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_handle_readonly&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;shutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rmtree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_or_folder_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;LookupError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;delete_file_or_folder() input is neither file nor folder: &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;file_or_folder_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GitDirectory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Class to represent a directory containing a git repository.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&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;git_dir_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_branch_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Initialize GitDirectory class.

        Args:
            git_dir_path (str): git directory path, i.e. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/path/to/git-repo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.
        &lt;/span&gt;&lt;span class="sh"&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;git_dir_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;git_dir_path&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;git_dir_path_parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;git_dir_path&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;entry&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;git_repo_name&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;git_dir_path_parts&lt;/span&gt;&lt;span class="p"&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;git_branch&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="nf"&gt;get_git_branch&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;base_branch_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base_branch_name&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;git_outstanding_changes&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="nf"&gt;get_git_outstanding_changes&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;_get_output_of_command&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;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Execute provided command and save terminal outputs to self&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s class attributes.

        Raises:
            RuntimeError: Thrown if command returned an error.

        Args:
            command (str): Terminal command to execute.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="c1"&gt;# This method works by running the command with "&amp;gt; filename.txt" argument to save outputs to file,
&lt;/span&gt;        &lt;span class="c1"&gt;# then reads that file for the output and deletes it before exiting.
&lt;/span&gt;        &lt;span class="n"&gt;temp_output_filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;-command-output-tempfile.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &amp;amp;&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;temp_output_filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp_output_filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;temp_output_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;temp_output_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;temp_output_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="nf"&gt;delete_file_or_folder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp_output_filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&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;temp_output_content&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp_output_content&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;_get_git_command_output&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;git_command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return output of provided git command in another directory.

        Args:
            git_command (str): Git command, i.e. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;git example-command&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.

        Returns:
            str: Output of provided git command, after it is changed to &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;git -C /path/to/git-repo example-command&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;git_command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;git_command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;git &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;git -C &lt;/span&gt;&lt;span class="si"&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;git_dir_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_get_output_of_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;git_command&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&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;get_git_branch&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="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return current branch in git repository.

        Returns:
            str: Current branch in git repository, i.e. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FEATURE-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_get_git_command_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;git branch --show-current&lt;/span&gt;&lt;span class="sh"&gt;"&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;get_git_outstanding_changes&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="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Get git outstanding changes status message.

        Returns:
            str: git outstanding changes status message, i.e. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9 files changed, 70 insertions(+), 25 deletions(-)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.
                Will return &lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="s"&gt; if there are no uncommitted changes.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;git_diff_stat&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="nf"&gt;_get_git_command_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;git diff --stat&lt;/span&gt;&lt;span class="sh"&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;git_diff_stat&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="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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="nf"&gt;strip&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;has_changes&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="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return True if git repository has outstanding, uncommited changes.

        Returns:
            bool: True if git repository has outstanding, uncommited changes.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;return&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;git_outstanding_changes&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_pointed_to_base_branch&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="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return True if git repository is pointed to the base branch.

        Returns:
            bool: True if git repository is pointed to the base branch.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;return&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;git_branch&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;base_branch_name&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_mermaid_element&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="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Get Mermaid representation of GitRepository object.

        Returns:
            str: Mermaid representation of GitRepository object, i.e.
                &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;path_to_git-repo[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;b&amp;gt;git-repo&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;FEATURE-1&amp;lt;br&amp;gt;All changes committed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]:::NoChangesNotPointedToBase&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jinja2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MERMAID_ELEMENT_TEMPLATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;element_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_&lt;/span&gt;&lt;span class="sh"&gt;"&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;git_dir_path_parts&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;git_repo_name&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;git_repo_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;git_branch&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;git_branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;git_outstanding_changes&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;git_outstanding_changes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;has_changes&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="nf"&gt;has_changes&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;is_pointed_to_base_branch&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="nf"&gt;is_pointed_to_base_branch&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="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&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;get_mermaid_routes&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="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Get all Mermaid element routes, pointing the connections between directories and their subdirectories
        inferred from the path to this git repository.

        Returns:
            list[str]: List of all mermaid element routes inferred from the path to this current git repository, i.e.
                [&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;path[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;] --&amp;gt; path_to&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;path_to[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;] --&amp;gt; path_to_git-repo&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;]
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;mermaid_routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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="nf"&gt;len&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;git_dir_path_parts&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
            &lt;span class="n"&gt;element_one_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_&lt;/span&gt;&lt;span class="sh"&gt;"&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;git_dir_path_parts&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="n"&gt;element_one_name&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;git_dir_path_parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&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="n"&gt;element_two_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_&lt;/span&gt;&lt;span class="sh"&gt;"&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;git_dir_path_parts&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt; &lt;span class="n"&gt;index&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="n"&gt;mermaid_routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;element_one_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;element_one_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;] --&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;element_two_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&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;mermaid_routes&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_all_git_dir_paths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root_dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Get all git directories (including subdirectores) from root where script is being called.

    Returns:
        list[str]: git directory paths, i.e. [&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/path/to/git-repo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;].
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;git_dir_paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dirs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&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="nf"&gt;abspath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root_dir&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;  &lt;span class="c1"&gt;# pylint: disable=unused-variable
&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;dir_name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dirs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;dir_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&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="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dir_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;os&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="nf"&gt;isdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;dir_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/.git&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;
            &lt;span class="n"&gt;git_dir_paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dir_path&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;git_dir_paths&lt;/span&gt;


&lt;span class="nd"&gt;@click.command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;CLI command to return a mermaid diagram of all git repositories - including in subdirectories in a specified
local folder along with the branch they&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;re currently pointed to and their number of uncommitted changes.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@click.option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--root-dir&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Relative or absolute path to directory to traverse. Defaults to treating current dir where the script is
being run as the root.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@click.option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--only-errors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;show_only_errors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;is_flag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;If True, hide any git repositories that are pointed to the base branch and have no outstanding changes.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@click.option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output_filename&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;If provided, save Mermaid string to file with filename.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@click.option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;git_repo_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;If provided, filter results to the provided git repository name.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&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;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;root_dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;show_only_errors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;output_filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;git_repo_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;CLI command to return a mermaid diagram of all git repositories - including in subdirectories in a specified
    local folder along with the branch they&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;re currently pointed to and their number of uncommitted changes.

    Args:
        root_dir (str, optional): Relative or absolute path to directory to traverse. Defaults to &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; to current dir.
        show_only_errors (bool, optional): If True, hide any git repositories that are pointed to the base branch and
            have no outstanding changes.
        output_filename (Optional[str], optional): If provided, save Mermaid string to file with filename.
        git_repo_name (Optional[str], optional): If provided, filter results to the provided git repository name.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;git_directories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;GitDirectory&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="nc"&gt;GitDirectory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;git_dir_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;git_dir_path&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;get_all_git_dir_paths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;git_repo_name&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;git_directories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;git_directory&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;git_directory&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;git_directories&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;git_directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;git_repo_name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;git_repo_name&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;mermaid_elements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&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="n"&gt;mermaid_routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;git_directory&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;git_directories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# If --only-errors, skip git repositories that are pointed to the base branch without outstanding changes.
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;show_only_errors&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;git_directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_pointed_to_base_branch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;git_directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has_changes&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="c1"&gt;# If --name provided, skip git repositores that do not have the same git repository name provided.
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;git_repo_name&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;git_directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;git_repo_name&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;git_repo_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="n"&gt;mermaid_elements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;git_directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_mermaid_element&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;mermaid_routes&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;git_directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_mermaid_routes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;mermaid_routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mermaid_routes&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jinja2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MERMAID_FULL_TEMPLATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;mermaid_full_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;mermaid_elements&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mermaid_elements&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;mermaid_routes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mermaid_routes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;output_filename&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mermaid_full_str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output_filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;output_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;output_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mermaid_full_str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>How and Why to Use Patches</title>
      <dc:creator>Kathryn DiPippo</dc:creator>
      <pubDate>Tue, 24 Jun 2025 22:35:03 +0000</pubDate>
      <link>https://dev.to/kdipippo/how-and-why-to-use-patches-3enp</link>
      <guid>https://dev.to/kdipippo/how-and-why-to-use-patches-3enp</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Background&lt;/li&gt;
&lt;li&gt;What is a Patch?&lt;/li&gt;
&lt;li&gt;Why and When to Patch?&lt;/li&gt;
&lt;li&gt;
How to Create and Apply a Patch

&lt;ul&gt;
&lt;li&gt;Create wrapper install command&lt;/li&gt;
&lt;li&gt;Initialize blank-slate state for third-party dependencies&lt;/li&gt;
&lt;li&gt;Make changes until third-party dependency is functional&lt;/li&gt;
&lt;li&gt;Create and assess initial patchfile result&lt;/li&gt;
&lt;li&gt;Refine patchfile content&lt;/li&gt;
&lt;li&gt;Create post-install patching step&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

List of Patching Commands

&lt;ul&gt;
&lt;li&gt;Command for Creating a Patch&lt;/li&gt;
&lt;li&gt;Command for Applying a Patch&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;At one of my previous jobs, one of our major headaches was installing and maintaining a single sign-on authentication PHP module called &lt;a href="https://simplesamlphp.org/" rel="noopener noreferrer"&gt;simpleSAMLphp&lt;/a&gt;. SSO is a positive on many fronts: end-users don't have to save a password for a new software application and can trust the fine folks at Google or Microsoft for keeping their accounts secure and setting up 2-factor-authentication, and we as developers don't have to dedicate resources towards maintaining and enforcing secure login practices and software architecture.&lt;/p&gt;

&lt;p&gt;But the way that we set up simpleSAMLphp was more complicated than it needed to be. The major woe we ran into were how these config files were first maintained. The block of simpleSAMLphp code was copied into our codebase and then directly modified in order to work with the rest of the codebase. This was perfectly fine on the first installation, but then the upgrades would come later. What if we had modified the configuration over time? How do we know what changes need to be applied to the module's newer version after it's put in place? Oftentimes, configuration changes that were made in a previous simpleSAMLphp version were missed on upgrade, and the team would scramble trying to find out what steps had been missed in the midst of a login outage.&lt;/p&gt;

&lt;p&gt;It's this kind of scenario where &lt;strong&gt;patches&lt;/strong&gt; come into play. We could have minimized these headaches had we followed the process of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Documenting the basic installation process of the simpleSAMLphp codebase into our repo&lt;/li&gt;
&lt;li&gt;Maintained patchfiles that would apply all the custom changes we need to make to get simpleSAMLphp working with our existing code&lt;/li&gt;
&lt;li&gt;Upgrades mean changing the version of simpleSAMLphp installed in the first bullet followed by recreating our patchfile content against the newer version where possible&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is a Patch?
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;patch&lt;/strong&gt; is a dedicated file that dictates how a code change should be applied to an existing file. In pratice, the content of a patchfile looks like a &lt;code&gt;diff&lt;/code&gt; (because it's generated using the &lt;a href="https://www.man7.org/linux/man-pages/man1/diff.1.html" rel="noopener noreferrer"&gt;&lt;code&gt;diff&lt;/code&gt; Linux command&lt;/a&gt;). Here's an example that modifies the content of &lt;code&gt;cli.js&lt;/code&gt; in the &lt;a href="https://www.npmjs.com/package/doiuse-email" rel="noopener noreferrer"&gt;doiuse-email&lt;/a&gt; NPM package to start with the line &lt;code&gt;#!/usr/bin/env node&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;--- node_modules/doiuse-email/bin/cli.js 2024-05-24 15:26:26
&lt;/span&gt;&lt;span class="gi"&gt;+++ cli.js 2024-05-24 15:26:02
&lt;/span&gt;&lt;span class="p"&gt;@@ -1,3 +1,4 @@&lt;/span&gt;
&lt;span class="gi"&gt;+#!/usr/bin/env node
&lt;/span&gt; import { program } from 'commander';
 import * as fs from 'node:fs';
 import process from 'node:process';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Node projects install third-party dependencies via the &lt;code&gt;npm install&lt;/code&gt; command. On my end, I need to either update how &lt;code&gt;package.json&lt;/code&gt; is configured (which informs how &lt;code&gt;npm install&lt;/code&gt; should run) or I need to make a bundled command such that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;My dependencies are installed&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Then&lt;/em&gt; my patches are applied.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why and When to Patch?
&lt;/h2&gt;

&lt;p&gt;Patches are best used in scenarios where we need to keep what changes &lt;em&gt;we're&lt;/em&gt; making very clearly defined from what the base codebase of a third-party dependency is. In the Background section, it would have been beneficial for my team to keep our team's configuration changes separate from the defaults provided to us by the simpleSAMLphp codebase being imported.&lt;/p&gt;

&lt;p&gt;Patches are also useful if we're making manual modifications to any codebase that is being auto-generated, i.e. if we're using &lt;a href="https://openapi-generator.tech/" rel="noopener noreferrer"&gt;openapi-generator&lt;/a&gt; off of an OpenAPI spec to create clients and scaffolded server codebases. We would want to maintain the patch as a way to ensure that subsequent re-generations of the code don't wipe out any manual changes we have to make in-between.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Always think about the future: if in 2 years I need to make an update to some part of our codebase that creates / updates a large swath of code, how likely will I remember this tiny manual change I made in one file?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Additionally, in cases where the third-party dependency being imported is open-source, consider whether the change that needs to be made would be beneficial to others using the codebase. If the change is specific to us, then leaving it as a patch is best. If the change is actually resolving a bug that other people using the third-party dependency are encountering, then consider being a good open-source software samaritan and start a PR with the contents of the patch applied. For efficiency:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create the patchfile with the bugfix on your team's repository&lt;/li&gt;
&lt;li&gt;Create the PR on the open-source repository and work through their validations and reviews&lt;/li&gt;
&lt;li&gt;Once the fix is released and the new version of the open-source dependency is tagged, update your team's repository with the newer version and remove the patchfile&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a helpful flowchart to navigate whether to create a patch and whether that patch should propagate upstream into its open-source project:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F8fdh31q44f67uphi3sla.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F8fdh31q44f67uphi3sla.png" alt="Image description" width="543" height="1078"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: above diagram was made using &lt;a href="https://mermaid.js.org/" rel="noopener noreferrer"&gt;Mermaid&lt;/a&gt; and its &lt;a href="https://mermaid.js.org/syntax/flowchart.html" rel="noopener noreferrer"&gt;flowchart syntax&lt;/a&gt; with the following snippet:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    one[Import &amp;lt;i&amp;gt;Dependency&amp;lt;/i&amp;gt; into our codebase]
    two{Does &amp;lt;i&amp;gt;Dependency&amp;lt;/i&amp;gt; work with our codebase as-is?}
    three[No Patches needed]
    four[Create Patches of changes needed for &amp;lt;i&amp;gt;Dependency&amp;lt;/i&amp;gt;]
    five{Is &amp;lt;i&amp;gt;Dependency&amp;lt;/i&amp;gt; open-source &amp;amp; would our changes be beneficial to others?}
    six[Create a PR against &amp;lt;i&amp;gt;Dependency&amp;lt;/i&amp;gt;'s repository.]

    one --&amp;gt; two
    two --&amp;gt;|No| four
    two --&amp;gt;|Yes| three
    four --&amp;gt; five
    five --&amp;gt;|Yes| six
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to Create and Apply a Patch
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create wrapper install command
&lt;/h3&gt;

&lt;p&gt;To get started, assume that there's a third-party dependency that is imported and whose contents you can and need to manipulate. For this section, I will walkthrough a scenario where that dependency is an NPM package. But this could apply to other language's package managers (like PHP's &lt;a href="https://packagist.org/" rel="noopener noreferrer"&gt;packagist&lt;/a&gt;) or where a giant &lt;code&gt;.zip&lt;/code&gt; of the codebase needs to be unzipped and hooked up (as was the case with &lt;a href="https://simplesamlphp.org/" rel="noopener noreferrer"&gt;simpleSAMLphp&lt;/a&gt;). In this scenario, &lt;code&gt;node_modules/&lt;/code&gt; will also be &lt;code&gt;.gitignore&lt;/code&gt;d and does not need to stay in the codebase for the patch to be applied.&lt;/p&gt;

&lt;p&gt;Start by wrapping how all third-party dependencies are installed in one singular, bundled command (i.e. create a &lt;a href="https://dev.to/kdipippo/how-to-configure-makefile-to-dynamically-generate-list-of-commands-37dc"&gt;Makefile command&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;install&lt;/span&gt;
&lt;span class="nl"&gt;install&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;          &lt;span class="c"&gt;##&lt;/span&gt;&lt;span class="nf"&gt; Install all third-party dependencies.&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; node_modules
    npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: your Makefile will need to update the indent in this codeblock from 4-spaces to 1-tab.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Initialize blank-slate state for third-party dependencies
&lt;/h3&gt;

&lt;p&gt;Run &lt;code&gt;make install&lt;/code&gt; to start with what the "default" state will be for your dependencies. At this point, your third-party dependencies are installed but not properly working with your codebase. I will refer to the NPM package that needs to be patched as &lt;code&gt;third-party/&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make changes until third-party dependency is functional
&lt;/h3&gt;

&lt;p&gt;Work inside the &lt;code&gt;node_modules/third-party/&lt;/code&gt; folder in coordination with manual testing and automated unit-testing until the codebase behaves as we need it to. Don't worry about being as reckless as possible or having to keep track of which files are being modified. If the code is too destroyed, the &lt;code&gt;make install&lt;/code&gt; command can be re-run to return to the blank-slate state.&lt;/p&gt;

&lt;p&gt;In this example, &lt;code&gt;node_modules/third-party/&lt;/code&gt; was finally functional after a number of changes were applied to the following 3 files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bin/cli.js
src/main.js
src/config.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create and assess initial patchfile result
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://www.man7.org/linux/man-pages/man1/diff.1.html" rel="noopener noreferrer"&gt;&lt;code&gt;diff&lt;/code&gt; Linux command&lt;/a&gt; is used to create the actual content of the patchfiles. In order to figure out what changes were actually performed, we'll need to &lt;code&gt;diff&lt;/code&gt; the entire folder we changed against its blank-slate state.&lt;/p&gt;

&lt;p&gt;Copy the changed folder to a different location:&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="nb"&gt;mkdir &lt;/span&gt;third-party-fixed
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; node_modules/third-party &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Optional) Rename the copied folder to clearly distinguish it in later steps:&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="nb"&gt;mv &lt;/span&gt;third-party third-party-fixed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Re-install our dependencies back to blank-slate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create and save a &lt;code&gt;diff&lt;/code&gt; of both folders:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-r&lt;/code&gt; is to run the &lt;code&gt;diff&lt;/code&gt; command recursively on both folders (or else &lt;code&gt;result.patch&lt;/code&gt; just lists the similar folders 1-level deep in each folder).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-u&lt;/code&gt; is to specify that the &lt;code&gt;diff&lt;/code&gt; content returned is in the &lt;a href="http://www.gnu.org/software/diffutils/manual/html_node/Unified-Format.html" rel="noopener noreferrer"&gt;Unified diff format&lt;/a&gt;. Without a number passed in with &lt;code&gt;-u&lt;/code&gt;, the output diff will also show 3 (default) lines of matching content before-and-after each altered line.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diff &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; node_modules/third-party third-party-fixed &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; result.patch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The content of &lt;code&gt;result.patch&lt;/code&gt; will show the full updates that were made in the Make changes until third-party dependency is functional section. Here is an example of this full patch with same list of files changed from earlier:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;code&gt;result.patch&lt;/code&gt; without refinement:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --color -r -u node_modules/third-party/bin/cli.js third-party-fixed/bin/cli.js
&lt;/span&gt;&lt;span class="gd"&gt;--- node_modules/third-party/bin/cli.js 2025-06-24 17:32:22
&lt;/span&gt;&lt;span class="gi"&gt;+++ third-party-fixed/bin/cli.js 2025-06-24 17:36:23
&lt;/span&gt;&lt;span class="p"&gt;@@ -9,6 +9,7 @@&lt;/span&gt;
     .option('--supported-features', 'output the supported email features for the email clients specified')
     .argument('[file]', 'the path to the HTML file to check')
     .action((file, options) =&amp;gt; {
&lt;span class="gi"&gt;+    throw new Error("TEST - stopping here to check how the prior methods work.");
&lt;/span&gt;     if (options.supportedFeatures !== undefined &amp;amp;&amp;amp; file !== undefined) {
         throw new Error('Only one of `file` or `--supported-features` should be provided.');
     }
&lt;span class="gh"&gt;diff --color -r -u node_modules/third-party/src/config.json third-party-fixed/src/config.json
&lt;/span&gt;&lt;span class="gd"&gt;--- node_modules/third-party/src/config.json 2025-06-24 17:33:00
&lt;/span&gt;&lt;span class="gi"&gt;+++ third-party-fixed/src/config.json 2025-06-24 17:35:23
&lt;/span&gt;&lt;span class="p"&gt;@@ -9,7 +9,7 @@&lt;/span&gt;
     "_sessionKey": "MyReallySecretPassword1",
     "_port": 443,
     "_aliasPort": 443,
&lt;span class="gd"&gt;-    "_redirPort": 80,
&lt;/span&gt;&lt;span class="gi"&gt;+    "_redirPort": 121,
&lt;/span&gt;     "_redirAliasPort": 80
   },
   "domains": {
&lt;span class="p"&gt;@@ -17,7 +17,7 @@&lt;/span&gt;
       "_title": "MyServer",
       "_title2": "Servername",
       "_minify": true,
&lt;span class="gd"&gt;-      "_newAccounts": true,
&lt;/span&gt;&lt;span class="gi"&gt;+      "_newAccounts": false,
&lt;/span&gt;       "_userNameIsEmail": true
     }
   },
&lt;span class="gh"&gt;diff --color -r -u node_modules/third-party/src/main.js third-party-fixed/src/main.js
&lt;/span&gt;&lt;span class="gd"&gt;--- node_modules/third-party/src/main.js 2025-06-24 17:33:40
&lt;/span&gt;&lt;span class="gi"&gt;+++ third-party-fixed/src/main.js 2025-06-24 17:35:02
&lt;/span&gt;&lt;span class="p"&gt;@@ -91,6 +91,7 @@&lt;/span&gt;
&lt;span class="err"&gt;
&lt;/span&gt; // Convert a string into a blob
 module.exports.data2blob = function (data) {
&lt;span class="gi"&gt;+    console.log("TEST - AT START OF module.exports.data2blob");
&lt;/span&gt;     var bytes = new Array(data.length);
     for (var i = 0; i &amp;lt; data.length; i++) bytes[i] = data.charCodeAt(i);
     var blob = new Blob([new Uint8Array(bytes)]);
&lt;span class="p"&gt;@@ -170,9 +171,13 @@&lt;/span&gt;
     return doc;
 };
 module.exports.unEscapeLinksFieldName = function (doc) {
&lt;span class="gi"&gt;+    console.log("TEST - 0 - module.exports.unEscapeLinksFieldName");
&lt;/span&gt;     if (doc.links != null) { for (var j in doc.links) { var ue = module.exports.unEscapeFieldName(j); if (ue !== j) { doc.links[ue] = doc.links[j]; delete doc.links[j]; } } }
&lt;span class="gi"&gt;+    console.log("TEST - 1 - module.exports.unEscapeLinksFieldName");
&lt;/span&gt;     if (doc.ssh != null) { for (var j in doc.ssh) { var ue = module.exports.unEscapeFieldName(j); if (ue !== j) { doc.ssh[ue] = doc.ssh[j]; delete doc.ssh[j]; } } }
&lt;span class="gi"&gt;+    console.log("TEST - 2 - module.exports.unEscapeLinksFieldName");
&lt;/span&gt;     if (doc.rdp != null) { for (var j in doc.rdp) { var ue = module.exports.unEscapeFieldName(j); if (ue !== j) { doc.rdp[ue] = doc.rdp[j]; delete doc.rdp[j]; } } }
&lt;span class="gi"&gt;+    console.log("TEST - 3 - module.exports.unEscapeLinksFieldName");
&lt;/span&gt;     return doc;
 };
 //module.exports.escapeAllLinksFieldName = function (docs) { for (var i in docs) { module.exports.escapeLinksFieldName(docs[i]); } return docs; };
&lt;span class="p"&gt;@@ -194,13 +199,13 @@&lt;/span&gt;
 module.exports.validateUsername = function (username, minlen, maxlen) { return (module.exports.validateString(username, minlen, maxlen) &amp;amp;&amp;amp; (username.indexOf(' ') == -1) &amp;amp;&amp;amp; (username.indexOf('"') == -1) &amp;amp;&amp;amp; (username.indexOf(',') == -1)); };
 module.exports.isAlphaNumeric = function (str) { return (str.match(/^[A-Za-z0-9]+$/) != null); };
 module.exports.validateAlphaNumericArray = function (array, minlen, maxlen) { if (((array != null) &amp;amp;&amp;amp; Array.isArray(array)) == false) return false; for (var i in array) { if ((typeof array[i] != 'string') || (module.exports.isAlphaNumeric(array[i]) == false) || ((minlen != null) &amp;amp;&amp;amp; (array[i].length &amp;lt; minlen)) || ((maxlen != null) &amp;amp;&amp;amp; (array[i].length &amp;gt; maxlen)) ) return false; } return true; };
&lt;span class="gd"&gt;-module.exports.getEmailDomain = function(email) {
-    if (!module.exports.validateEmail(email, 1, 1024)) {
-        return '';
-    }
-    const i = email.indexOf('@');
-    return email.substring(i + 1).toLowerCase();
-}
&lt;/span&gt;&lt;span class="gi"&gt;+// module.exports.getEmailDomain = function(email) {
+//     if (!module.exports.validateEmail(email, 1, 1024)) {
+//         return '';
+//     }
+//     const i = email.indexOf('@');
+//     return email.substring(i + 1).toLowerCase();
+// }
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt; module.exports.validateEmailDomain = function(email, allowedDomains) {
     // Check if this request is for an allows email domain
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;For this example, I can see the 3 files that were changed earlier and what was done to them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In &lt;code&gt;bin/cli.js&lt;/code&gt;, I put a temporary &lt;code&gt;throw new Error()&lt;/code&gt; for debugging purposes&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;src/main.js&lt;/code&gt;, I added a handful of &lt;code&gt;console.log()&lt;/code&gt;s for debugging purposes and commented out the &lt;code&gt;getEmailDomain()&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;src/config.json&lt;/code&gt;, I changed the values for two keys in that config &lt;code&gt;_redirPort&lt;/code&gt; and &lt;code&gt;_newAccounts&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If instead you know which specific files were affected and only want to get the patchfile content for a single file, rerun the command without the &lt;code&gt;-r&lt;/code&gt; and with each argument against a single file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diff &lt;span class="nt"&gt;-u&lt;/span&gt; node_modules/third-party/src/config.json third-party-fixed/src/config.json &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; file.patch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Refine patchfile content
&lt;/h3&gt;

&lt;p&gt;It's essential that the content of the patch itself is minimal and essential in order to make it much easier to re-apply to updated versions of the dependency in the future. If a patch in the future needs to be created that overlaps with an existing patch, those two patches should be combined to form one new patch to avoid a complicated layering of changes and potential conflicts.&lt;/p&gt;

&lt;p&gt;In the example, after reviewing the giant patchfile created, I've determined that only the changes in &lt;code&gt;src/config.json&lt;/code&gt; are needed. The immediate benefits of the patchfile are now clear: the two config changes we need to maintain are small and easy-to-miss. It's easy to see how future upgrades may neglect to update these two values and re-encounter the same issues just worked through:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --color -r -u node_modules/third-party/src/config.json third-party-fixed/src/config.json
&lt;/span&gt;&lt;span class="gd"&gt;--- node_modules/third-party/src/config.json 2025-06-24 17:33:00
&lt;/span&gt;&lt;span class="gi"&gt;+++ third-party-fixed/src/config.json 2025-06-24 17:35:23
&lt;/span&gt;&lt;span class="p"&gt;@@ -9,7 +9,7 @@&lt;/span&gt;
     "_sessionKey": "MyReallySecretPassword1",
     "_port": 443,
     "_aliasPort": 443,
&lt;span class="gd"&gt;-    "_redirPort": 80,
&lt;/span&gt;&lt;span class="gi"&gt;+    "_redirPort": 121,
&lt;/span&gt;     "_redirAliasPort": 80
   },
   "domains": {
&lt;span class="p"&gt;@@ -17,7 +17,7 @@&lt;/span&gt;
       "_title": "MyServer",
       "_title2": "Servername",
       "_minify": true,
&lt;span class="gd"&gt;-      "_newAccounts": true,
&lt;/span&gt;&lt;span class="gi"&gt;+      "_newAccounts": false,
&lt;/span&gt;       "_userNameIsEmail": true
     }
   },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whether you use one patchfile to apply changes across a number of files or separate patchfiles for each file changed is up-to-you. I personally have only had to write patches for changes against one file. I also would personally lean towards one-patchfile-per-file as a way to better understand the scope of the changes and to more easily work through upgrades in cases where one of the files is removed or dramatically altered with that upgrade.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create post-install patching step
&lt;/h3&gt;

&lt;p&gt;Your repository should contain a new folder &lt;code&gt;patches/&lt;/code&gt; that contains all of the patchfiles need to be applied after installing third-party dependencies. The organization of this folder entirely depends on how complex of a patching setup is needed.&lt;/p&gt;

&lt;p&gt;If there are a lot of third-party dependencies that each need a lot of patches, then consider setting up a more complex folder structure to more easily navigate and understand where changes are being applied. But for our example and for most use cases, storing all patchfiles flat in the &lt;code&gt;patches/&lt;/code&gt; folder is fine.&lt;/p&gt;

&lt;p&gt;I recommend the following naming convention by joining the following identifiers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ticket ID where the patch needs to be created (i.e. the Ticket ID to install and configure the third-party dependency)&lt;/li&gt;
&lt;li&gt;Name of the third-party dependency&lt;/li&gt;
&lt;li&gt;Version of that third-party dependency where this patch is applied&lt;/li&gt;
&lt;li&gt;File path to where the change is applied with &lt;code&gt;/&lt;/code&gt; forward-slashes replaced with &lt;code&gt;_&lt;/code&gt; underscores&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For our previous example, &lt;code&gt;result.patch&lt;/code&gt; should be moved into &lt;code&gt;patches/&lt;/code&gt; with the name &lt;code&gt;PROJ-123_third-party_1.0.0_src_config.json.patch&lt;/code&gt;. Also optionally, the content of this patch can be modified to remove the temporary path where the secondary file was stored. This is entirely for cosmetic purposes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- diff --color -r -u node_modules/third-party/src/config.json third-party-fixed/src/config.json
&lt;/span&gt;&lt;span class="gi"&gt;+ diff --color -r -u node_modules/third-party/src/config.json config.json
&lt;/span&gt;  --- node_modules/third-party/src/config.json 2025-06-24 17:33:00
&lt;span class="gd"&gt;- +++ third-party-fixed/src/config.json 2025-06-24 17:35:23
&lt;/span&gt;&lt;span class="gi"&gt;+ +++ config.json 2025-06-24 17:35:23
&lt;/span&gt;  @@ -9,7 +9,7 @@
       "_sessionKey": "MyReallySecretPassword1",
       "_port": 443,
       "_aliasPort": 443,
  -    "_redirPort": 80,
  +    "_redirPort": 121,
       "_redirAliasPort": 80
     },
     "domains": {
  @@ -17,7 +17,7 @@
         "_title": "MyServer",
         "_title2": "Servername",
         "_minify": true,
  -      "_newAccounts": true,
  +      "_newAccounts": false,
         "_userNameIsEmail": true
       }
     }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;I'm happy to have had the opportunity to make a &lt;code&gt;diff&lt;/code&gt; on a &lt;code&gt;diff&lt;/code&gt; here LOL.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now, the wrapper install command from earlier needs to be modified to apply the patch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;.PHONY: install
&lt;span class="p"&gt;install:          ## Install all third-party dependencies.
&lt;/span&gt;    rm -rf node_modules
    npm install
&lt;span class="gi"&gt;+    patch node_modules/third-party/src/config.json patches/PROJ-123_third-party_1.0.0_src_config.json.patch
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: your Makefile will need to update the indent in this codeblock from 4-spaces to 1-tab.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Verify that the installation and patching is successfully by performing the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;make install&lt;/code&gt; to reset the dependencies back to blank-slate state and then programmatically apply the patch.&lt;/li&gt;
&lt;li&gt;Manually verify that the files specified in the patchfile contain those changes in your third-party dependency folder (i.e. check that changes that need to be applied to &lt;code&gt;config.json&lt;/code&gt; appear inside &lt;code&gt;node_modules/third-party/src/config.json&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Verify that your full codebase implementation now works without any other manual changes needed.&lt;/li&gt;
&lt;li&gt;If the third-party dependency is still unsuccessful, repeat steps Make changes until third-party dependency is functional through to this current step.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  List of Patching Commands
&lt;/h2&gt;

&lt;p&gt;In short, these are the two key Unix terminal commands:&lt;/p&gt;

&lt;h3&gt;
  
  
  Command for Creating a Patch
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;diff&lt;/code&gt; command arguments explained:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;first argument&lt;/strong&gt; is the "before" file left un-touched from what is provided in the third-party dependency codebase&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;second argument&lt;/strong&gt; is the "after" file with manual changes applied&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-r&lt;/code&gt; is to run the &lt;code&gt;diff&lt;/code&gt; command recursively on both folders (or else &lt;code&gt;result.patch&lt;/code&gt; just lists the similar folders 1-level deep in each folder).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-u&lt;/code&gt; is to specify that the &lt;code&gt;diff&lt;/code&gt; content returned is in the &lt;a href="http://www.gnu.org/software/diffutils/manual/html_node/Unified-Format.html" rel="noopener noreferrer"&gt;Unified diff format&lt;/a&gt;. Without a number passed in with &lt;code&gt;-u&lt;/code&gt;, the output diff will also show 3 (default) lines of matching content before-and-after each altered line.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;diff&lt;/code&gt; two files and save the result in one patchfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diff &lt;span class="nt"&gt;-u&lt;/span&gt; node_modules/third-party/src/config.json config-fixed.json &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; file.patch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;diff&lt;/code&gt; two folders and save the result in one patchfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diff &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; node_modules/third-party third-party-fixed &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; result.patch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Command for Applying a Patch
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;patch&lt;/code&gt; command arguments explained:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;first argument&lt;/strong&gt; is the original file left un-touched and located where the third-party dependency will be installed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;second argument&lt;/strong&gt; is the patchfile that specifies how the "first argument" file should be altered&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;patch&lt;/code&gt; example with a single file in &lt;code&gt;node_modules/&lt;/code&gt; with the patch stored in the &lt;code&gt;patches/&lt;/code&gt; folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;patch node_modules/third-party/src/config.json patches/PROJ-123_third-party_1.0.0_src_config.json.patch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>The Essential Software Development Process</title>
      <dc:creator>Kathryn DiPippo</dc:creator>
      <pubDate>Tue, 24 Jun 2025 19:13:05 +0000</pubDate>
      <link>https://dev.to/kdipippo/the-essential-software-development-process-4ohj</link>
      <guid>https://dev.to/kdipippo/the-essential-software-development-process-4ohj</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Background&lt;/li&gt;
&lt;li&gt;
Required Tooling

&lt;ul&gt;
&lt;li&gt;Issue Tracker&lt;/li&gt;
&lt;li&gt;Git Platform&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Getting Started

&lt;ul&gt;
&lt;li&gt;Setting up the Issue Tracker&lt;/li&gt;
&lt;li&gt;Setting up the Git Repository&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Walkthrough of the End-to-End Process

&lt;ul&gt;
&lt;li&gt;Issue PROJ-123 is ready to be worked on&lt;/li&gt;
&lt;li&gt;Branch PROJ-123 is created on the git repository&lt;/li&gt;
&lt;li&gt;Assignee pushes to PROJ-123 branch&lt;/li&gt;
&lt;li&gt;Assignee creates Pull Request for PROJ-123 branch&lt;/li&gt;
&lt;li&gt;Team reviews Pull Request for PROJ-123 branch&lt;/li&gt;
&lt;li&gt;Assignee squashes and merges PROJ-123 branch to main branch&lt;/li&gt;
&lt;li&gt;Assignee creates tag for PROJ-123 release&lt;/li&gt;
&lt;li&gt;Feature released in PROJ-123 causes an issue in production&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Recommended Enhancements to this Process

&lt;ul&gt;
&lt;li&gt;Add as many programmatic checks as possible&lt;/li&gt;
&lt;li&gt;Automate programmatic checks and releases with CI/CD Tooling&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;The first job I held right after graduating college was chaotic. Leadership was the key factor in all of its issues; the company had experienced a large turnover right before I was hired to fill in the gaps. But we lacked real tech leadership who could mentor and guide us on how to effectively develop in teams. I was very fortunate that my second job did offer that leadership and expertise for effectively solving problems.&lt;/p&gt;

&lt;p&gt;Often, I've fantasized, "what would I do differently at my first job knowing what I know now?" And the #1 answer that always comes out on top: &lt;strong&gt;I wish we used &lt;code&gt;git&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We had no version control at the time, and the problems we ran into were so obvious. There was no central codebase, so every member of our (albeit small) team was keeping copies of all of the code we had written. I would have dozens of &lt;code&gt;ProductDisplayPageFINAL_FINAL_JUNE10_FINAL.js&lt;/code&gt; on my machine in case a teammate of mine's changes would save over mine. Only our CTO had access to the codebase and all of the backups of files pushed into the server. So much time was spent asking him to go in and revert changes. Coworkers would be berated for "not checking" when other people had altered the code, even though there were no formal internal notifications set up. All of our code was being pushed up with entirely manual testing, and it frequently encountered bugs or issues. There was no concept of team-based code reviews. We were all isolated to our own work without knowing what other people were building and pushing.&lt;/p&gt;

&lt;p&gt;It was night-and-day just finding out how &lt;code&gt;git&lt;/code&gt; and good process could solve all of these problems. I often tell people that if it weren't for having gone through the headaches and hurt of working in places such as my first job that the lessons I learned at my second job would not have stuck. I believe that my career to this point has all served a purpose in instructing me on proper software practices.&lt;/p&gt;

&lt;p&gt;This is the essential software development process. I believe that any process that relies on code should start with this backbone and then be elaborated on over time in order to make processes more efficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Required Tooling
&lt;/h2&gt;

&lt;p&gt;There are two key tools that are needed for this process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Issue Tracker
&lt;/h3&gt;

&lt;p&gt;New feature requests, update requests, and bug reports need to be collected, prioritized, and assigned. No matter what platform is selected, ensure that Issues being reported can be linked to using an &lt;strong&gt;identifier&lt;/strong&gt; (ideally a short one).&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.atlassian.com/software/jira" rel="noopener noreferrer"&gt;Jira&lt;/a&gt; by Atlassian. Jira Issue IDs are of the format &lt;code&gt;PROJECTID-###&lt;/code&gt;, i.e. &lt;code&gt;JIRA-123&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://asana.com/" rel="noopener noreferrer"&gt;Asana&lt;/a&gt;. Asana Tasks will need to be configured with a &lt;a href="https://help.asana.com/s/article/id-custom-fields?language=en_US" rel="noopener noreferrer"&gt;Custom ID field&lt;/a&gt;, as ticket IDs via the API are all long UUIDs.&lt;/li&gt;
&lt;li&gt;If using only GitHub:

&lt;ul&gt;
&lt;li&gt;GitHub offers &lt;a href="https://docs.github.com/en/issues/tracking-your-work-with-issues/about-issues" rel="noopener noreferrer"&gt;"Issues" on its repositories&lt;/a&gt;. Consider though if using this feature whether issues being reported could be affecting code across other repositories if the code is split out. This may mean setting up a central "issue reporting" repo. The ticket ID would the the Issue # (i.e. "1", but should be used in branch names later as "issue-1").&lt;/li&gt;
&lt;li&gt;GitHub also offers a &lt;a href="https://docs.github.com/en/issues/planning-and-tracking-with-projects" rel="noopener noreferrer"&gt;"Project" management feature&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Git Platform
&lt;/h3&gt;

&lt;p&gt;A web platform should be used to make it easy to view code and navigate version history without solely relying on the &lt;code&gt;git&lt;/code&gt; tool. For this article, I will be using GitHub as the key facilitator. But all features here should be available across all other platforms as well.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bitbucket.org/product" rel="noopener noreferrer"&gt;BitBucket&lt;/a&gt; by Atlassian&lt;/li&gt;
&lt;li&gt;&lt;a href="https://about.gitlab.com/" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setting up the Issue Tracker
&lt;/h3&gt;

&lt;p&gt;As mentioned, whatever issue tracker platform is used, all tickets need to have a short unique identifier for each task. This will be easier to use in Git and to verbally relay to others for what is being worked (saying &lt;em&gt;"I'm working on the requested change to the email notifications to make the font size bigger"&lt;/em&gt; vs. saying &lt;em&gt;"I'm working on PROJ-123"&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;All issues should contain the full documentation for what change needs to be performed, and all issues should be reviewed as a group by the development team. I recommend an "Acceptance Criteria" checklist at the bottom of every task that clearly defines what steps need to be accomplished before an issue is marked as done. This checklist should remove any ambiguity when code changes need to be performed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up the Git Repository
&lt;/h3&gt;

&lt;p&gt;For this article, the git repository will be referred to as &lt;code&gt;git-project-repo&lt;/code&gt;. This repo should have a default branch &lt;code&gt;main&lt;/code&gt; whose codebase is treated as "stable".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Flwi3i67rtkxo5oal5jcs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Flwi3i67rtkxo5oal5jcs.png" alt=" " width="254" height="135"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: all of the &lt;code&gt;git&lt;/code&gt; diagrams in this article were made using &lt;a href="https://mermaid.js.org/" rel="noopener noreferrer"&gt;Mermaid&lt;/a&gt; and its &lt;a href="https://mermaid.js.org/syntax/gitgraph.html" rel="noopener noreferrer"&gt;gitGraph syntax&lt;/a&gt;. Dev.to does not support Mermaid markdown at this time, so all diagrams were saved as PNGs and are posted here with their accompanying Mermaid snippet.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gitGraph
    commit id:"initial commit"
    commit id:"PROJ-121 | Feature 1" tag:"1.0.0"
    commit id:"PROJ-122 | Feature 2" tag:"1.1.0" type:HIGHLIGHT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This repo has had 2 features created for it. The production implementation should also rely on the "tag"s for the commits and not just relying on what's in &lt;code&gt;main&lt;/code&gt;. Production in this case is pointed to tag &lt;code&gt;1.1.0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Additionally, the git repository should be set up with a Pull Request Template (if using GitHub, this should be a markdown file stored in &lt;code&gt;.github/PULL_REQUEST_TEMPLATE.md&lt;/code&gt;; this will automatically populate all PR base comments with this file). This Template should contain the following sections:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Link to Issue: &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;PROJ-123&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://issue-tracking.platform.com/PROJ-123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="gh"&gt;# Motivation&lt;/span&gt;

Why is this change needed?

&lt;span class="gh"&gt;# Changes Made&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; High-level checklist of changes performed
&lt;span class="p"&gt;-&lt;/span&gt; Could be the list of commits if change is small

&lt;span class="gh"&gt;# How to Verify&lt;/span&gt;

Procedure for either the Issue assignee to self-validate their work or for PR reviewers to review the work here, in addition to reviewing the code changes themselves.

&lt;span class="gh"&gt;# Release Steps&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; [ ] Checklist of steps to perform for releases
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Could be a link to a full procedure document
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Walkthrough of the End-to-End Process
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Issue PROJ-123 is ready to be worked on
&lt;/h3&gt;

&lt;p&gt;An Issue with short unique ID &lt;code&gt;PROJ-123&lt;/code&gt;, as mentioned in the Setting up the Issue Tracker section, contains the complete request that needs to be actioned. This issue has been reviewed and agreed-upon by the whole developer team and could be theoretically picked up by any member to action. The team has now agreed that &lt;code&gt;PROJ-123&lt;/code&gt; is ready to start now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Branch PROJ-123 is created on the git repository
&lt;/h3&gt;

&lt;p&gt;The assignee of &lt;code&gt;PROJ-123&lt;/code&gt; creates a new branch on the &lt;code&gt;git-project-repo&lt;/code&gt; repository. The branch name is the same as the Issue's ID.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F16zaysjftolw1v0slas2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F16zaysjftolw1v0slas2.png" alt=" " width="287" height="145"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gitGraph
    commit id:"initial commit"
    commit id:"PROJ-121 | Feature 1" tag:"1.0.0"
    commit id:"PROJ-122 | Feature 2" tag:"1.1.0" type:HIGHLIGHT
    branch "PROJ-123"
    checkout "PROJ-123"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Assignee pushes to PROJ-123 branch
&lt;/h3&gt;

&lt;p&gt;The Issue itself should be broken down into the separate steps needed to finish the "Acceptance Criteria". For example, a development task may be broken down into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Refactor a large function into smaller functions&lt;/li&gt;
&lt;li&gt;Update one of the smaller functions to take in a new argument&lt;/li&gt;
&lt;li&gt;Update unit-tests to pass&lt;/li&gt;
&lt;li&gt;Call that updated function in a different part of the codebase&lt;/li&gt;
&lt;li&gt;Add new unit-tests for new functionality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these will be separate commits pushed by the developer into the Issue's branch. I recommend prefixing all commits with the Issue's ID as well, in case there are PRs that end up containing a handful of requests (i.e. if an Issue &lt;code&gt;PROJ-234&lt;/code&gt; is a small typo that needs to be fixed that would be more convenient to tackle at the same time as this request).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fgnqgdj79yiz4l0n4sycy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fgnqgdj79yiz4l0n4sycy.png" alt=" " width="587" height="312"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gitGraph
    commit id:"initial commit"
    commit id:"PROJ-121 | Feature 1" tag:"1.0.0"
    commit id:"PROJ-122 | Feature 2" tag:"1.1.0" type:HIGHLIGHT
    branch "PROJ-123"
    checkout "PROJ-123"
    commit id:"PROJ-123 | Refactor large func into smaller funcs"
    commit id:"PROJ-123 | Update smaller func with new arg"
    commit id:"PROJ-123 | Update unit-tests to pass"
    commit id:"PROJ-123 | Added call to updated function"
    commit id:"PROJ-123 | Add new unit-tests"
    commit id:"PROJ-234 | Fix typo in docs"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Assignee creates Pull Request for PROJ-123 branch
&lt;/h3&gt;

&lt;p&gt;By now, all steps of the "Acceptance Criteria" are now addressed, and all local validations by the assignee are passing. The assignee is now ready to get the change reviewed by the rest of the team. The assignee now creates a Pull Request to request to merge changes from the &lt;code&gt;PROJ-123&lt;/code&gt; branch into the &lt;code&gt;main&lt;/code&gt; branch.&lt;/p&gt;

&lt;p&gt;For this example, the Pull Request's top comment will contain the following filled-in template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Link to Issue: &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;PROJ-123&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://issue-tracking.platform.com/PROJ-123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
Also addresses: &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;PROJ-234&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://issue-tracking.platform.com/PROJ-234&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="gh"&gt;# Motivation&lt;/span&gt;

New feature request to change the size of the logo from 40 pixels tall to 50 pixels. This requires refactoring a part of the codebase in order to accept a new parameter. But this will let us make it easier to adjust this size in the future via a config update instead of a code change.

Also fixes a typo in the documentation reported in &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;PROJ-234&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://issue-tracking.platform.com/PROJ-234&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;.

&lt;span class="gh"&gt;# Changes Made&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Refactor a large function into smaller functions
&lt;span class="p"&gt;-&lt;/span&gt; Update one of the smaller functions to take in a new argument
&lt;span class="p"&gt;-&lt;/span&gt; Update unit-tests to pass
&lt;span class="p"&gt;-&lt;/span&gt; Call that updated function in a different part of the codebase
&lt;span class="p"&gt;-&lt;/span&gt; Add new unit-tests for new functionality
&lt;span class="p"&gt;-&lt;/span&gt; Fixed typo in documentation

&lt;span class="gh"&gt;# How to Verify&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; [x] &lt;span class="sb"&gt;`make tests`&lt;/span&gt; runs all unit-tests locally
&lt;span class="p"&gt;-&lt;/span&gt; [x] Go to https://internal-test.platform/feature-3 to see the changes in our test environment

&lt;span class="gh"&gt;# Release Steps&lt;/span&gt;

Current tag: &lt;span class="sb"&gt;`1.1.0`&lt;/span&gt;. New tag: &lt;span class="sb"&gt;`1.2.0`&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; [ ] Send email to internal@company.com "Incoming release"
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Squash and merge PR into "main" branch
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Create new tag &lt;span class="sb"&gt;`1.2.0`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Manually navigate into https://internal.platform/admin and change the tag to &lt;span class="sb"&gt;`1.2.0`&lt;/span&gt; and press "restart platform"
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Validate changes in Prod environment
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt; ] If rollback is required, follow the [Rollback Procedure&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://github.com/git-project-repo/README.md#rollback-procedure&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The content of this PR template is important because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All code changes should be clearly tied to a "why we need this change" and a "how this change was done" for historical references&lt;/li&gt;
&lt;li&gt;Reviewers are given a clear set of instructions for reviewing work&lt;/li&gt;
&lt;li&gt;Assignees are given a clear set of instructions for releasing work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Any part of this process that is ambiguous will turn into delays (because people are afraid of pushing code or performing a change that will cause problems). Clearly defined procedures (that can be expanded on if there are problems that arise) will remove this worry and hesitation.&lt;/p&gt;

&lt;p&gt;The original Issue Tracking Ticket should also be updated with a link to the new Pull Request and indicate that the work is now ready to review.&lt;/p&gt;

&lt;h3&gt;
  
  
  Team reviews Pull Request for PROJ-123 branch
&lt;/h3&gt;

&lt;p&gt;I personally recommend going simple with this. I like to use the "labels" feature of GitHub and allow users to tag PRs with a "needs review" label that can be filtered against all repositories in a GitHub organization. The benefits of this strategy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PRs can be started and refined until they're properly ready to review. For example, an assignee might ensure that the "diff" doesn't contain any accidental changes after making a proper PR.&lt;/li&gt;
&lt;li&gt;Using the label adequately communicates the status of this work. PRs that have a lot of feedback that may warrant significant rework may be untagged with the "needs review" label before it is re-added again.&lt;/li&gt;
&lt;li&gt;All members of the team can see all PRs that are tagged for review and can theoretically receive feedback from anyone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your team is large enough, another strategy is to randomly assign all PRs to dedicated reviewers. This also has the added benefit of ensuring that knowledge is being properly shared across the team and prevents a small handful of team members from becoming the sole reviewers. But I have not run into issues with the label-only strategy that couldn't be fixed by asking members of the team to slow down with code reviews to see if that forces others on the team to boost their contributions.&lt;/p&gt;

&lt;p&gt;By the end of this step, any PR that is tagged for review should ideally have a minimum of &lt;strong&gt;2 approvals&lt;/strong&gt; if possible. Just having 1 approval is enough to get another pair of eyes on changes being pushed, but 2 will also allow for adequate knowledge transfer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Assignee squashes and merges PROJ-123 branch to main branch
&lt;/h3&gt;

&lt;p&gt;In the Pull Request template, the PR for &lt;code&gt;PROJ-123&lt;/code&gt; contains the following checklist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Release Steps&lt;/span&gt;

Current tag: &lt;span class="sb"&gt;`1.1.0`&lt;/span&gt;. New tag: &lt;span class="sb"&gt;`1.2.0`&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; [ ] Send email to internal@company.com "Incoming release"
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Squash and merge PR into "main" branch
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Create new tag &lt;span class="sb"&gt;`1.2.0`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Manually navigate into https://internal.platform/admin and change the tag to &lt;span class="sb"&gt;`1.2.0`&lt;/span&gt; and press "restart platform"
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Validate changes in Prod environment
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt; ] If rollback is required, follow the [Rollback Procedure&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://github.com/git-project-repo/README.md#rollback-procedure&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an example of what steps might be needed for a full push to production. Your team may want to expand on these steps where necessary (like if there are any database backup commands that need to be performed before releases). If, for example, a pre-production environment should be first used to validate the work in a semi-live environment, I recommend expanding that process to still rely on &lt;code&gt;git&lt;/code&gt;'s versioning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;# Release Steps
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;Current tag: `1.1.0`. New tag: `1.2.0`
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt; - [ ] Send email to internal@company.com "Incoming release"
 - [ ] Squash and merge PR into "main" branch
 - [ ] Create new tag `1.2.0`
&lt;span class="gi"&gt;+- [ ] Manually navigate into https://internal-preprod.platform/admin and change the tag to `1.2.0` and press "restart platform"
+- [ ] Validate changes in Pre-Prod environment
&lt;/span&gt; - [ ] Manually navigate into https://internal.platform/admin and change the tag to `1.2.0` and press "restart platform"
 - [ ] Validate changes in Prod environment
 - [ ] If rollback is required, follow the [Rollback Procedure](https://github.com/git-project-repo/README.md#rollback-procedure)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the PR is ready to be squashed and merged into the &lt;code&gt;main&lt;/code&gt; branch. From the PR, the commit history will look like this full diagram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F6w0brx8av9k04sutaktz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F6w0brx8av9k04sutaktz.png" alt=" " width="637" height="312"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gitGraph
    commit id:"initial commit"
    commit id:"PROJ-121 | Feature 1" tag:"1.0.0"
    commit id:"PROJ-122 | Feature 2" tag:"1.1.0" type:HIGHLIGHT
    branch "PROJ-123"
    checkout "PROJ-123"
    commit id:"PROJ-123 | Refactor large func into smaller funcs"
    commit id:"PROJ-123 | Update smaller func with new arg"
    commit id:"PROJ-123 | Update unit-tests to pass"
    commit id:"PROJ-123 | Added call to updated function"
    commit id:"PROJ-123 | Add new unit-tests"
    commit id:"PROJ-234 | Fix typo in docs"
    checkout "main"
    merge "PROJ-123" id:"PROJ-123 | Feature 3"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But from &lt;code&gt;main&lt;/code&gt; (and after the &lt;code&gt;PROJ-123&lt;/code&gt; branch is deleted now that its work is complete), the history will appear more simply as:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fi9ocbutttdkgglcev7nk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fi9ocbutttdkgglcev7nk.png" alt=" " width="304" height="135"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gitGraph
    commit id:"initial commit"
    commit id:"PROJ-121 | Feature 1" tag:"1.0.0"
    commit id:"PROJ-122 | Feature 2" tag:"1.1.0" type:HIGHLIGHT
    commit id:"PROJ-123 | Feature 3"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deleting the branch does not mean the history is gone forever. In GitHub, any commit on &lt;code&gt;main&lt;/code&gt; resulting from a PR will contain a link to the original PR in its title. Additionally, the &lt;a href="https://docs.github.com/en/repositories/working-with-files/using-files/viewing-and-understanding-files#viewing-the-line-by-line-revision-history-for-a-file" rel="noopener noreferrer"&gt;git blame feature&lt;/a&gt; will enable us to easily check &lt;em&gt;any&lt;/em&gt; line of code to find out any "why" and "how" the change was performed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Assignee creates tag for PROJ-123 release
&lt;/h3&gt;

&lt;p&gt;Looking at the git graph, note that a new version has not yet been tagged for the new commit. There should be no live environments who are using this code in their implementation. As noted in the example "Release Steps" checklist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; [x] Squash and merge PR into "main" branch
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Create new tag &lt;span class="sb"&gt;`1.2.0`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Manually navigate into https://internal.platform/admin and change the tag to &lt;span class="sb"&gt;`1.2.0`&lt;/span&gt; and press "restart platform"
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Validate changes in Prod environment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tagging is essential in order to easily revert or release changes together without needing to work through code reversion PRs. With the "pre-production" example noted in the Assignee squashed and mergeds PROJ-123 branch to main branch section, tagging makes it easy to understand what environments are viewing which codebase at-a-glance.&lt;/p&gt;

&lt;p&gt;Tags should follow the &lt;a href="https://semver.org/" rel="noopener noreferrer"&gt;Semantic Versioning standard&lt;/a&gt; because it is easy-to-follow and well-documented.&lt;/p&gt;

&lt;p&gt;The assignee now tags the new release with "1.2.0" because it is a new feature that is not backwards-incompatible with the existing codebase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fbbt0hf5nh6s24u87x774.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fbbt0hf5nh6s24u87x774.png" alt=" " width="304" height="135"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gitGraph
    commit id:"initial commit"
    commit id:"PROJ-121 | Feature 1" tag:"1.0.0"
    commit id:"PROJ-122 | Feature 2" tag:"1.1.0" type:HIGHLIGHT
    commit id:"PROJ-123 | Feature 3" tag:"1.2.0"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The assignee now navigates into the production environment and switches the tag for its code reference from "1.1.0" to "1.2.0".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fqwedj26lct5s4kig4ebo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fqwedj26lct5s4kig4ebo.png" alt=" " width="304" height="135"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gitGraph
    commit id:"initial commit"
    commit id:"PROJ-121 | Feature 1" tag:"1.0.0"
    commit id:"PROJ-122 | Feature 2" tag:"1.1.0"
    commit id:"PROJ-123 | Feature 3" tag:"1.2.0" type:HIGHLIGHT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By now, all the steps for releasing &lt;code&gt;PROJ-123&lt;/code&gt; are complete. The original Issue Tracking Ticket should also be updated with a comment on the release's completed status and any outcomes encountered.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feature released in PROJ-123 causes an issue in production
&lt;/h3&gt;

&lt;p&gt;Oh no! The release that was pushed ended up surfacing an incident in production! This issue may be something that is noticed immediately or results in a critical bug issue with ID &lt;code&gt;PROJ-124&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Incidents like these should be planned as a matter of &lt;em&gt;when&lt;/em&gt; and not &lt;em&gt;if&lt;/em&gt;; code is written and maintained by humans. Having procedures in place in advance is crucial and necessary and will instill trust in the team moving forward.&lt;/p&gt;

&lt;p&gt;There are two strategies that can be applied here: &lt;strong&gt;fail-forward&lt;/strong&gt; or &lt;strong&gt;rollback&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Fail-forward means that the root cause of the issue was quickly located in the codebase. Maybe there's a small typo in a config name that wasn't caught with linting. Fail-forward means whoever is addressing this fire creates a PR with this changes and gets it reviewed and approved following all the steps in the Walkthrough of the End-to-End Process again.&lt;/p&gt;

&lt;p&gt;Rollback means either that the root cause of the issue can't be located quickly enough to go through with failing-forward &lt;em&gt;or&lt;/em&gt; that the fire being caused is too great to allow for time to investigate the root cause right away. Luckily, rollback is easy with our current configuration by switching the tag back to the last "stable" version of the codebase. There may be other steps as well for rolling back, like if an older version of a backend database needs to be restored.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fbbt0hf5nh6s24u87x774.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fbbt0hf5nh6s24u87x774.png" alt=" " width="304" height="135"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gitGraph
    commit id:"initial commit"
    commit id:"PROJ-121 | Feature 1" tag:"1.0.0"
    commit id:"PROJ-122 | Feature 2" tag:"1.1.0" type:HIGHLIGHT
    commit id:"PROJ-123 | Feature 3" tag:"1.2.0"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whatever issue is encountered here, we are luckily able to effectively figure out these problems because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Version control makes it fast for us to switch between "safe" and "new" releases.&lt;/li&gt;
&lt;li&gt;All releases will be small, modular changes with clearly outlined &lt;code&gt;diff&lt;/code&gt;s. Releases should not be a signficant amount of alterations that make it difficult to pin-point the culprit.&lt;/li&gt;
&lt;li&gt;Every part of this process has a documented procedure that can be altered or expanded to cover any gap that was missing where this original issue was introduced (like if an extra unit-test or check would have caught this culprit when the PR was in review).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Recommended Enhancements to this Process
&lt;/h2&gt;

&lt;p&gt;The Walkthrough of the End-to-End Process now covers all of the basics for using &lt;code&gt;git&lt;/code&gt; as part of any code change process. Here are all the ways that this backbone can be streamlined.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add as many programmatic checks as possible
&lt;/h3&gt;

&lt;p&gt;I firmly believe that any code or machine-readable specifications should be stored in version control and reviewed by all members of a team. This includes Markdown documentation as well as XML-as-code files (such as any no-code software services that saves its "code" as XML).&lt;/p&gt;

&lt;p&gt;For any work being version controlled, I recommend looking into tooling to automate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Formatting&lt;/strong&gt;: Automatically re-structure content to a single standard (i.e. forcing code from being tab-indented to space-indented).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linting&lt;/strong&gt;: Checking if content adheres to a set of rules that require manual fixes (i.e. checking that all functions defined in code contain a top-level comment explaining what the function does and what its arguments mean).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unit-testing&lt;/strong&gt;: Run a full suite of automated checks against a code implementation. This ensures both backwards-compatibility as well as that new features match expected behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More programmatic checks that are set up against repositories should also result in faster and more thoughtful code reviews. Human reviews should not be wasted on trivial checks like "this file is missing a newline at the end of it" and should instead be thoughtful and along the lines of "maybe it would be more maintainable for us to do things &lt;em&gt;this way&lt;/em&gt; instead of &lt;em&gt;that way&lt;/em&gt;".&lt;/p&gt;

&lt;p&gt;Every time there is an issue like what takes place in Feature released in PROJ-123 causes an issue in production, consider whether a new programmatic check or an expansion to an existing programmatic check would have caught the issue during code reviews.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automate programmatic checks and releases with CI/CD Tooling
&lt;/h3&gt;

&lt;p&gt;Now that more programmatic checks are set up, it's time to fully automate them into this process using CI/CD tooling.&lt;/p&gt;

&lt;p&gt;CI/CD tooling can be used for both the "code review" phase (to automate all the "How to Verify" checkboxes) as well as the "release" phase (to automate all the "Release Steps" checkboxes).&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/features/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.jenkins.io/" rel="noopener noreferrer"&gt;Jenkins&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.travis-ci.com/" rel="noopener noreferrer"&gt;Travis CI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I am a personal fan of navigating through DevOps with the following mindset:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write a manual procedure for accomplishing a task and follow it enough times until it's set in stone. The procedure can now function as pseudocode for any automation implementation.&lt;/li&gt;
&lt;li&gt;Convert the manual procedure to a streamlined programmatic command&lt;/li&gt;
&lt;li&gt;Invoke the programmatic command in a CI/CD workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This mindset ensures that time isn't wasted troubleshooting the programmatic or CI/CD configuration until the team fully knows whether its useful to incorporate it into their processes (or to know if the process is too time consuming to continue doing manually).&lt;/p&gt;

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

&lt;p&gt;The process outlined here is not a reinvented wheel. For further reading, I recommend reading any guide about &lt;strong&gt;Trunk-Based Development&lt;/strong&gt;, the actual terminology for this process (the "trunk" is the &lt;code&gt;main&lt;/code&gt; branch on a git repository).&lt;/p&gt;

&lt;p&gt;Software development by now is a solved science, and it's just a matter of understanding and utilizing these best practices. I'm thankful for having had the opportunity to experience the pain of a software development team that greatly struggled without having a system like this in place.&lt;/p&gt;

&lt;p&gt;By following this process, I can personally guarantee that the following issues will be resolved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No more asking why a line of code was added or when it was added&lt;/li&gt;
&lt;li&gt;No more worry about having your work wiped out by someone else's code change. Team members can work against the same file in their own branches without worrying about stepping on someone else's toes (this will require navigating a merge conflict, but a merge conflict is way better than losing your work entirely).&lt;/li&gt;
&lt;li&gt;All members of the team are aware of all changes being worked on across all codebases and have the opportunity to ask questions and keep open communication during code reviews. No more knowledge silos during software development.&lt;/li&gt;
&lt;li&gt;All review, release, and rollback processes are documented and can be followed by anyone on the team without having a single-person-of-failure.&lt;/li&gt;
&lt;li&gt;No more worry about performing rollbacks. All changes being pushed are easy to investigate for root causes.&lt;/li&gt;
&lt;li&gt;Any team can become a well-oiled machine.&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Strategies to reduce "visual" code complexity</title>
      <dc:creator>Kathryn DiPippo</dc:creator>
      <pubDate>Sun, 06 Apr 2025 14:55:20 +0000</pubDate>
      <link>https://dev.to/kdipippo/strategies-to-reduce-visual-code-complexity-5fa2</link>
      <guid>https://dev.to/kdipippo/strategies-to-reduce-visual-code-complexity-5fa2</guid>
      <description>&lt;p&gt;Code readability is a big factor to that maintenance process. Code that is written well is easy and fast to read when problems arise. Same with infrastructure, it's never about just the initial build. Those structures need to be built with maintenance in mind: how to extend the longevity of a project to make the hours spent building it worthwhile. We need to care about the full lifecycle of any feature at every step.&lt;/p&gt;

&lt;p&gt;Understanding how to reduce "visual" complexity is key, not just code complexity (although both, in my opinion, go hand-in-hand: code that has less cyclomatic complexity is often easier to read).&lt;/p&gt;

&lt;p&gt;This article contains some general philosophies I've adopted with my approach to programming, specifically with how to better arrange logic statements. I am not the pioneer of these concepts and will link to other blog posts that I feel are more articulate about this than I am.&lt;/p&gt;

&lt;p&gt;All examples are written with Python, but this applies to all programming languages with logic operators.&lt;/p&gt;

&lt;h1&gt;
  
  
  Sections
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Go completely "else"-less&lt;/li&gt;
&lt;li&gt;"Flip" logic statements to arrange by size of inner content&lt;/li&gt;
&lt;li&gt;Implement "getters" where possible over "setters"&lt;/li&gt;
&lt;li&gt;Try/Catch the smallest statement possible&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Go completely "else"-less
&lt;/h1&gt;

&lt;p&gt;Avoiding using &lt;code&gt;else&lt;/code&gt; statements entirely has been the most dramatic but effect change I've enacted to write better code. &lt;code&gt;else&lt;/code&gt; is convenient but masks the actual condition being hit before code is run.&lt;/p&gt;

&lt;p&gt;Suppose I have the following code:&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;if&lt;/span&gt; &lt;span class="n"&gt;condition_1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟦&lt;/span&gt;&lt;span class="sh"&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;condition_2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟨&lt;/span&gt;&lt;span class="sh"&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟥&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What is the true logic that results in "🟥" getting printed? If that line was actually an exception thrown, how can I quickly understand this code in order to address the issue at hand? The example here is a smaller implementation of what might be more complicated logic routing and inner blocks of code.&lt;/p&gt;

&lt;p&gt;Instead, consider firstly explicitly writing what each &lt;code&gt;else&lt;/code&gt; means, as in the negative of the previous &lt;code&gt;if&lt;/code&gt; statement:&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;if&lt;/span&gt; &lt;span class="n"&gt;condition_1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟦&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;condition_1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;condition_2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟨&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;condition_2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟥&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Being able to articulatly write out each &lt;code&gt;else&lt;/code&gt; may make it easier to find where guard clauses could be used, or where it would be more readable to combine the sub-conditions together. I am personally a fan of migrating this type of block into a function and returning with each &lt;code&gt;if&lt;/code&gt; statement hit:&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;def&lt;/span&gt; &lt;span class="nf"&gt;print_color&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;condition_1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟦&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;condition_2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟨&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟥&lt;/span&gt;&lt;span class="sh"&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 only exception to &lt;code&gt;else&lt;/code&gt; would be for very small snippets of code where it is convenient to do so, such as Python's version of a ternary operator:&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="n"&gt;new_variable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Value1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;condition_1&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Value2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another example is if I need to shove a mapped value into a method where it makes sense for me to NOT keep the mapped value for other purposes:&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="nf"&gt;run_weird_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;input_arg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Value1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;condition_1&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Value2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Strs "Value1" and "Value2" are not referenced anywhere else in the code.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But in general, my recommendation is to move this to a function and ensure that the function name is straight-forward when read later throughout the codebase:&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;def&lt;/span&gt; &lt;span class="nf"&gt;get_variable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;condition_1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Value1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Value2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Recommended articles for further reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer20.com/elseless/" rel="noopener noreferrer"&gt;Why We Should Avoid Using &lt;code&gt;else&lt;/code&gt; in Programming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://refactoring.guru/replace-nested-conditional-with-guard-clauses" rel="noopener noreferrer"&gt;Replace Nested Conditional with Guard Clauses&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  "Flip" logic statements to arrange by size of inner content
&lt;/h1&gt;

&lt;p&gt;This goes hand-in-hand with the removal of ambiguous &lt;code&gt;else&lt;/code&gt; statements: the shortest amount of code should get run first to allow for the larger blocks of code to lie "flat" / run with less indentation. Guard clauses are a good example, where the "content" is typically a single line of code to raise an exception.&lt;/p&gt;

&lt;p&gt;Take this code snippet 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;for&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;full_list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;condition_1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟦&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟦&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟦&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟦&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟦&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟦&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟦&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟦&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟦&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;condition_2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟨&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟨&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟨&lt;/span&gt;&lt;span class="sh"&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="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟥&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order for me to understand the &lt;code&gt;else&lt;/code&gt; here, I need to physically scroll far up the code and mentally negate the other two conditions. I use the &lt;a href="https://marketplace.visualstudio.com/items?itemName=oderwat.indent-rainbow" rel="noopener noreferrer"&gt;indent-rainbow VSCode extension&lt;/a&gt; to help with this type of troubleshooting, but this feature requires me to load the code locally. GitHub's code viewer does not have a feature at this time to easily show what &lt;code&gt;if&lt;/code&gt; each &lt;code&gt;else&lt;/code&gt; is tied to.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;RuntimeError()&lt;/code&gt; here should be migrated to the top as a proper guard clause that clearly states &lt;em&gt;why&lt;/em&gt; it is getting run. I do this by "flipping" the logic: negate the condition and move it first.&lt;/p&gt;

&lt;p&gt;Following it, the 3 "🟨" statements should be checked and run first before the 9 "🟦" statements. I am also a fan of adding a comment for any "implicit" logic statements being checked as a result of this re-arranging:&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;for&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;full_list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;condition_1&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;not_condition_2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟥&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;condition_2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟨&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟨&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟨&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;

    &lt;span class="c1"&gt;# if condition_1:
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟦&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟦&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟦&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟦&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟦&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟦&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟦&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟦&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🟦&lt;/span&gt;&lt;span class="sh"&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;code&gt;continue&lt;/code&gt; is my friend here for statements like this. But ideally, I would migrate any logic that needs to "exit early" into a separate function with proper &lt;code&gt;return&lt;/code&gt; statements.&lt;/p&gt;

&lt;h1&gt;
  
  
  Implement "getters" where possible over "setters"
&lt;/h1&gt;

&lt;p&gt;In my experience, it is way more difficult to both unittest and debug a function if the output of the command is to modify an existing structure in-place. Instead, write individual getters for separate actions performed and then stitch them together into clearly-defined setters&lt;/p&gt;

&lt;p&gt;For example, say I have an initial &lt;code&gt;dict&lt;/code&gt; and want to instead return a different &lt;code&gt;dict&lt;/code&gt;:&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="n"&gt;before_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="c1"&gt;# Called "before_dict.json" in later example.
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;00001&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2025-01-01&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jdoe&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Lorem&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;00002&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2025-01-02&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jsmith&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Ipsum&lt;/span&gt;&lt;span class="sh"&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;# ... code goes here ...
&lt;/span&gt;
&lt;span class="n"&gt;after_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="c1"&gt;# Called "after_dict.json" in later example.
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;00001&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;entry&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2025-01-01 | jdoe | Lorem&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;00002&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;entry&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2025-01-02 | jsmith | Ipsum&lt;/span&gt;&lt;span class="sh"&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;p&gt;One strategy might be to write a method that modifies in-place the input &lt;code&gt;dict&lt;/code&gt; with the updated values:&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;def&lt;/span&gt; &lt;span class="nf"&gt;modify_input_dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;input_dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;input_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;entry&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; | &lt;/span&gt;&lt;span class="sh"&gt;"&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="p"&gt;[&lt;/span&gt;
                &lt;span class="n"&gt;input_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="n"&gt;input_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="n"&gt;input_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&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;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;input_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;input_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;input_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this code will perform the task at hand, the lack of rigidity of the structure makes it more confusing to understand what the data looks like during its execution. We might need to do extra checks in other parts of the code (i.e. to check for the existing of keys like &lt;code&gt;date&lt;/code&gt;) due to the lack of enforcement in this first upstream method. Another idea would be to not delete the keys in this example and to instead attach the &lt;code&gt;entry&lt;/code&gt; field to the &lt;code&gt;dict&lt;/code&gt;; but over time, that might cause this structure to become very large and filled with redundant fields.&lt;/p&gt;

&lt;p&gt;Unit-testing with this approach also becomes a necessity: the stored mocks for the input and output would be useful as "documentation" for the modification being performed.&lt;/p&gt;

&lt;p&gt;Instead, I recommend always writing methods that return and then using those as "setters" if absolutely necessary:&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;def&lt;/span&gt; &lt;span class="nf"&gt;get_entry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_modified_input_dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;modified_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;input_dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;modified_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;entry&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;entry&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;get_entry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;input_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="n"&gt;input_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="n"&gt;input_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&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;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;modified_dict&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The method cleanly defines the "before" and "after" in its own implementation. I understand the structure of output because I can see the whole thing here without needing to navigate to the unit-test fixtures. Writing unit-tests against this method is also easier: I can test the "actual" output against an "expected" output with a fed in "input" without needing to keep track of one or more variables for modifications:&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;def&lt;/span&gt; &lt;span class="nf"&gt;test_before&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Tests for the implementation where data is modified in-place.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;actual_output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;before_dict.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# We confusingly name this "output" despite it being the input mock.
&lt;/span&gt;    &lt;span class="nf"&gt;modify_input_dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actual_output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;expected_output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;after_dict.json&lt;/span&gt;&lt;span class="sh"&gt;"&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;actual_output&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected_output&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_after&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Tests for the implementation where getters are used instead.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;input_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;before_dict.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;actual_output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_modified_input_dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;expected_output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;after_dict.json&lt;/span&gt;&lt;span class="sh"&gt;"&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;actual_output&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected_output&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Try/Catch the smallest statement possible
&lt;/h1&gt;

&lt;p&gt;Suppose I have the following function:&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;def&lt;/span&gt; &lt;span class="nf"&gt;perform_task&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;value1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_variable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;value2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_variable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;func_might_throw_runtime_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;value3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;func_might_throw_assertion_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;func_might_throw_runtime_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;value3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;transform_values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;func_might_throw_runtime_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;value3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&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;value3&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the logs return a RuntimeError, how do I know which statement here was the root cause? What if the message returned from &lt;code&gt;func_might_throw_runtime_error()&lt;/code&gt; is the same regardless of the input being sent in? Maybe we don't have control over this method, so we lack the ability to update the message with the specific erroring value.&lt;/p&gt;

&lt;p&gt;At-a-glance, there is a lot going on to adequately drill into the problem without cross-referencing with the stack trace. We will eventually find the problem, but it will take extra, unnecessary lost time to get there.&lt;/p&gt;

&lt;p&gt;Instead, each try/except should be set to wrap exactly the amount of code where the error can be encountered:&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;def&lt;/span&gt; &lt;span class="nf"&gt;perform_task&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;value1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_variable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;value2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_variable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;func_might_throw_runtime_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;RuntimeError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;runtime_error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error: value1 = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;value1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;runtime_error&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;value3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;func_might_throw_assertion_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;AssertionError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;assertion_error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;AssertionError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error: value1 = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;value1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;assertion_error&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;func_might_throw_runtime_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;RuntimeError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;runtime_error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error: value2 = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;value2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;runtime_error&lt;/span&gt;


    &lt;span class="n"&gt;value3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;transform_values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;func_might_throw_runtime_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;RuntimeError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;runtime_error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error: value3 = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;value3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;runtime_error&lt;/span&gt;

    &lt;span class="n"&gt;value3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&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;value3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above snippet serves as an example of what might be seen in practice. The resulting method is significantly larger but is much more useful when needing to drill into issues. We can clearly map each inner function called to the outer error message in our logs without the stack trace.&lt;/p&gt;

&lt;p&gt;We may also be making incorrect assumptions around the behaviors of the other methods present here too. What if &lt;code&gt;transform_values()&lt;/code&gt; also returns a RuntimeError with certain inputs? We can save ourselves so much heartache and debugging hours by updating our code to try/except wrap each method; this will surface these assumptions when they're encountered properly and allow us to go in and wrap those assumptions with better handling.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>My go-to Python snippets</title>
      <dc:creator>Kathryn DiPippo</dc:creator>
      <pubDate>Wed, 24 Apr 2024 13:19:49 +0000</pubDate>
      <link>https://dev.to/kdipippo/my-go-to-python-snippets-12pp</link>
      <guid>https://dev.to/kdipippo/my-go-to-python-snippets-12pp</guid>
      <description>&lt;p&gt;My list of go-to Python snippets that I frequently look up, all in one place! Most of the time, I'm just searching for how to parse files into Pythonic objects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Read File Into Words&lt;/li&gt;
&lt;li&gt;Read JSON File into Dict&lt;/li&gt;
&lt;li&gt;
Read CSV File into Dict List

&lt;ul&gt;
&lt;li&gt;Read CSV File into Dict List without "\ufeff"&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Read YAML File into Dict&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Read File Into Words
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_file_into_words&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Parse a text file into a 2D list of words, i.e.
        &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;This is an example file with
        multiple lines of text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; becomes
        [&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;This is an example file with&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;multiple lines of text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;].

    Args:
        filename (str): Path to text file.

    Returns:
        list[list[str]]: 2D list of string words.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;input_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readlines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&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="n"&gt;content&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Read JSON File into Dict
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_json_file_into_dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Parse a JSON file into a dictionary.

    Args:
        filename (str): Path to JSON file.

    Returns:
        dict: Dictionary representation of JSON file contents.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;json_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json_file&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;content&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Read CSV File into Dict List
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_csv_file_into_dict_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Parse a CSV file into a list of dictionaries.

    Args:
        filename (str): Path to CSV file.

    Returns:
        dict: List of dictionary representations of CSV file contents.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;csv_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DictReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&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;content&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Read CSV File into Dict List without "\ufeff"
&lt;/h3&gt;

&lt;p&gt;Modification of the previous snippet with an additional method that removes the &lt;code&gt;\ufeff&lt;/code&gt; special character if present in the column name.&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rename_dict_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;old_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Rename key in dictionary while preserving original key order.

    Args:
        input_dict (dict): Dictionary to apply key rename.
        old_key (str): Current name of key.
        new_key (str): New name of key.

    Returns:
        dict: Dictionary with applied key rename.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;old_key&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;new_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;input_dict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&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;read_csv_file_into_dict_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Parse a CSV file into a list of dictionaries.

    Args:
        filename (str): Path to CSV file.

    Returns:
        dict: List of dictionary representations of CSV file contents.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;text_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;read_file_into_words&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text_content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;csv_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DictReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;row_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;row_keys_to_rename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;current_key&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;current_key&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;row_dict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\ufeff&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;current_key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row_key_to_rename&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;row_keys_to_rename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;row_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rename_dict_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row_dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row_key_to_rename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row_key_to_rename&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\ufeff&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

            &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row_dict&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;content&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Read YAML File into Dict
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_yaml_into_dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return content of YAML file as dictionary.

    Args:
        filename (str): Path to YAML file to read and return.

    Returns:
        dict: Content of YAML file as dictionary.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;yaml_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;yaml_contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yaml_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&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;yaml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safe_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;yaml_contents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>How to configure Makefile to dynamically generate list of commands</title>
      <dc:creator>Kathryn DiPippo</dc:creator>
      <pubDate>Mon, 04 Mar 2024 17:55:38 +0000</pubDate>
      <link>https://dev.to/kdipippo/how-to-configure-makefile-to-dynamically-generate-list-of-commands-37dc</link>
      <guid>https://dev.to/kdipippo/how-to-configure-makefile-to-dynamically-generate-list-of-commands-37dc</guid>
      <description>&lt;p&gt;In past projects I've worked on, the first command inside the Makefile was always a manually maintained list of commands available in the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;list command-one command-two&lt;/span&gt;

&lt;span class="nl"&gt;list&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"command-one    This command performs A, B, and C"&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"command-two    This command performs X, Y, and Z"&lt;/span&gt;

&lt;span class="nl"&gt;command-one&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Example command"&lt;/span&gt;

&lt;span class="nl"&gt;command-two&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Example command"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But thanks to the Makefile set up in &lt;a href="https://github.com/rochacbruno/python-project-template/"&gt;this Python starter project&lt;/a&gt;, there's an even handier way to have &lt;code&gt;make list&lt;/code&gt; shown above be generated without updating two places.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;At the top of the Makefile&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;help&lt;/span&gt;
&lt;span class="nl"&gt;help&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;             &lt;span class="c"&gt;##&lt;/span&gt;&lt;span class="nf"&gt; Show the help.&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Usage: make &amp;lt;target&amp;gt;"&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Targets:"&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;fgrep &lt;span class="s2"&gt;"##"&lt;/span&gt; Makefile | fgrep &lt;span class="nt"&gt;-v&lt;/span&gt; fgrep
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;fgrep&lt;/code&gt; here is the key, where the Makefile itself is parsed for any comments prefixed with &lt;code&gt;##&lt;/code&gt; and printing the entire lines. Any comments with just one &lt;code&gt;#&lt;/code&gt; will be ignored. With the &lt;code&gt;help&lt;/code&gt; command itself, the second line is what is &lt;code&gt;fgrep&lt;/code&gt;-ed and printed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;help&lt;/span&gt;
&lt;span class="nl"&gt;help&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;             &lt;span class="c"&gt;##&lt;/span&gt;&lt;span class="nf"&gt; Show the help.&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Usage: make &amp;lt;target&amp;gt;"&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Targets:"&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;fgrep &lt;span class="s2"&gt;"##"&lt;/span&gt; Makefile | fgrep &lt;span class="nt"&gt;-v&lt;/span&gt; fgrep

&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;command-one&lt;/span&gt;
&lt;span class="nl"&gt;command-one&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="c"&gt;##&lt;/span&gt;&lt;span class="nf"&gt; This command performs A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nf"&gt; B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nf"&gt; and C&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Example command"&lt;/span&gt;
    &lt;span class="c"&gt;# Inner comment to describe script.&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"run shell script"&lt;/span&gt;

&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;command-two&lt;/span&gt;
&lt;span class="nl"&gt;command-two&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="c"&gt;##&lt;/span&gt;&lt;span class="nf"&gt; This command performs X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nf"&gt; Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nf"&gt; and Z&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Example command"&lt;/span&gt;

&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;command-hidden&lt;/span&gt;
&lt;span class="nl"&gt;command-hidden&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="c"&gt;#&lt;/span&gt;&lt;span class="nf"&gt; This command performs initialization&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Example hidden command"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The results of running &lt;code&gt;make&lt;/code&gt; or &lt;code&gt;make help&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Usage: make &amp;lt;target&amp;gt;

Targets:
help:             ## Show the help.
command-one:      ## This command performs A, B, and C
command-two:      ## This command performs X, Y, and Z
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; for commands with dependencies to others, I found it useful to instead copy the command for the comment line and then have a second line for the dependencies, i.e.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;command-two&lt;/span&gt;
&lt;span class="nl"&gt;command-two&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="c"&gt;##&lt;/span&gt;&lt;span class="nf"&gt; This command performs X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nf"&gt; Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nf"&gt; and Z&lt;/span&gt;
&lt;span class="nl"&gt;command-two&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;clean build&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Example requires 'make clean' &amp;amp; 'make build'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thought this was a really interesting configuration and wanted to share. Hope you find it useful too!&lt;/p&gt;

</description>
      <category>makefile</category>
      <category>learning</category>
      <category>development</category>
      <category>automation</category>
    </item>
    <item>
      <title>You don't need a personal website (and why I no longer have one)</title>
      <dc:creator>Kathryn DiPippo</dc:creator>
      <pubDate>Sat, 16 Oct 2021 07:16:05 +0000</pubDate>
      <link>https://dev.to/kdipippo/you-dont-need-a-personal-website-and-why-i-no-longer-have-one-3ap9</link>
      <guid>https://dev.to/kdipippo/you-dont-need-a-personal-website-and-why-i-no-longer-have-one-3ap9</guid>
      <description>&lt;p&gt;I often tell the story of how a simple website made me fall in love with programming. I was given a brief training assignment to build a website from scratch using HTML and CSS, with no prior knowledge beforehand. Something about the immediate back-and-forth between building the code and testing the display made something &lt;em&gt;click&lt;/em&gt;. Soon after, I would spend hours after work building neat dashboards and landing pages with server-less implementations. Google Sheets used as a replacement for databases, hundreds of lines of jQuery added for interactivity. All of this was incorporated into my personal site, which served as both a list of my projects and demonstration of my abilities throughout the years.&lt;/p&gt;

&lt;p&gt;Several years and hundreds of hours later, I am retiring my personal site. This decision was not easy to come to, but I know that my goals and abilities at this moment make it disadvantageous to keep it up.&lt;/p&gt;

&lt;h1&gt;
  
  
  Preface: Do you need a personal site?
&lt;/h1&gt;

&lt;p&gt;Before getting started, I would still advocate for creating your own personal site if there's a specific need that it fills. If you are a freelance website builder, a personal site is necessary as a way of showcasing your skills. Others with design-oriented positions may find that the complete creative control a personal site provides best projects your abilities than using other platforms like &lt;a href="https://dribbble.com/" rel="noopener noreferrer"&gt;Dribbble&lt;/a&gt;. Additionally, a personal site can be useful as a central resource if you have a sizeable audience, even if the only content for display is a list of your social media profiles.&lt;/p&gt;

&lt;p&gt;I do not fall into the above categories. The goals of my own personal site are different from the goals of the examples listed above. Here are my main reasons for going forward with the deprecation and what may be insightful if you are skeptical about your own personal website:&lt;/p&gt;

&lt;h1&gt;
  
  
  1) It's a large time sink
&lt;/h1&gt;

&lt;p&gt;There's just something about my &lt;em&gt;personal&lt;/em&gt; website that makes me devote way more time than necessary to updating it. Routine visits to update the content to the latest updates inevitably become updates to the layout as well. The colors never feel correct, the layout is too short or too large or too cluttered, there's never enough transitions, there's too many transitions, etc. Every attempt to revamp the layout spins to a large, excessive TODO.&lt;/p&gt;

&lt;p&gt;I certainly have a greater appreciation towards UI designers. I am my own nightmare client, constantly asking for small, inconsequential changes on the website and never feeling satisifed.&lt;/p&gt;

&lt;h1&gt;
  
  
  2) It's a larger time sink than it deserves to be
&lt;/h1&gt;

&lt;p&gt;I recommend this article for additional info, as well as some interesting insights in the comments:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/profydev" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&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%2Forganization%2Fprofile_image%2F3338%2F43691f8f-0edd-4c5e-bbc3-584d46d5f3c6.png" alt="Profy.dev"&gt;
      &lt;div class="ltag__link__user__pic"&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%2Fuser%2Fprofile_image%2F140139%2F5881453b-c3cb-4812-a9e8-4c41db313b70.jpg" alt=""&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/profydev/this-survey-among-60-hiring-managers-reveals-don-t-waste-your-time-on-a-react-portfolio-website-17ge" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Don't waste your time on a (React) portfolio website - 60+ hiring managers and a survey&lt;/h2&gt;
      &lt;h3&gt;Johannes Kettmann for Profy.dev ・ Aug 6 '21&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#react&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#career&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;The core summary boils down to a common sentiment around (software developer) hiring managers: a personal website is appealing to hiring managers but do not increase a candidate's chances. The primary emphasis and discussion in job interviews mostly focus on GitHub projects and contributions.&lt;/p&gt;

&lt;p&gt;In order for a personal website to be effective, it needs to be &lt;em&gt;fully functional&lt;/em&gt; (i.e. works with every phone and browser) and &lt;em&gt;updated&lt;/em&gt;. You either spend an excessive amount of time to satisfy these requirements and gain nothing &lt;em&gt;or&lt;/em&gt; risk being judged poorly if the website breaks while the hiring manager looks at it or it contains outdated information.&lt;/p&gt;

&lt;h1&gt;
  
  
  3) Other platforms fulfill the need for a personal website better
&lt;/h1&gt;

&lt;p&gt;Breaking it down, the problems that a personal website solve can boil down to the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A concise location with links to other platforms like LinkedIn, Twitter, etc.&lt;/li&gt;
&lt;li&gt;A portfolio with examples of project work with information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a programmer with a number of open source repositories, my GitHub profile is the best showcase of my skills as a developer. I could attach a link to these repos on my resume when reaching out to companies, but even that feels too low-level. Thankfully, &lt;a href="https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/personalizing-your-profile#adding-a-bio-to-your-profile" rel="noopener noreferrer"&gt;GitHub released an update in mid 2020 that added a customizable bio section visible on your profile page&lt;/a&gt;. I can highlight specific repositories and recent blog posts (if I was cool, I would automatically update a list of recent blog posts with a GitHub Actions workflow).&lt;/p&gt;

&lt;p&gt;Other platforms cover any other holes that a personal site would cover, namely linking to Dev.to as a blogging platform and linking to LinkedIn for my professional credentials. These platforms are way easier to maintain and keep updated, without the mental push to want to update the UI in tandem.&lt;/p&gt;

&lt;h1&gt;
  
  
  What should replace your personal website instead?
&lt;/h1&gt;

&lt;p&gt;Since GitHub will be the main focus of discussion in potential discussions with companies, it should also be the main focus when searching for a replacement to a personal site.&lt;/p&gt;

&lt;p&gt;The steps I am taking in the deprecation process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub will act as my work portfolio:

&lt;ul&gt;
&lt;li&gt;Update GitHub bio with clear links to my LinkedIn (for professional experience) and Dev.to (for blogging)&lt;/li&gt;
&lt;li&gt;Update GitHub projects with well defined READMEs&lt;/li&gt;
&lt;li&gt;Highlight a handful of my highlighted GitHub repositories. Write articles about their motivation and development.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;LinkedIn will act as a resume (although my actual resume is now hosted on &lt;a href="https://resume.io" rel="noopener noreferrer"&gt;resume.io&lt;/a&gt;)

&lt;ul&gt;
&lt;li&gt;Update LinkedIn to link back to GitHub and Dev.to profiles.&lt;/li&gt;
&lt;li&gt;Update profile to mimic Resume credentials list&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Dev.to will act as a blog

&lt;ul&gt;
&lt;li&gt;Update Dev.to profile to link back to GitHub and LinkedIn&lt;/li&gt;
&lt;li&gt;Write articles to fill in the remaining gaps a website would provide that a repo README can't fulfill, i.e. articles about obstacles encountered in projects and how you overcame them, reasoning behind selecting a specific toolchain, etc.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;All of this will amount to 3 social media platforms to represent myself professionally. The key and motivation was to create a network of clearly defined tools that are first-and-foremost easy to update.&lt;/p&gt;

&lt;p&gt;The true replacement for a personal site is something that will be updated on a regular basis above all else.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>github</category>
      <category>programming</category>
    </item>
    <item>
      <title>4 beginner-friendly Github Actions snippets</title>
      <dc:creator>Kathryn DiPippo</dc:creator>
      <pubDate>Wed, 09 Jun 2021 14:34:15 +0000</pubDate>
      <link>https://dev.to/kdipippo/4-beginner-friendly-github-actions-snippets-4n72</link>
      <guid>https://dev.to/kdipippo/4-beginner-friendly-github-actions-snippets-4n72</guid>
      <description>&lt;p&gt;GitHub Actions are extremely useful for those of you interested in setting up a CICD pipeline. Workflows are not only easy to implement but even easier to hook into your existing projects. These are a list of snippets that I found useful while trying to understand how to add these to my repos and how to develop my own.&lt;/p&gt;

&lt;p&gt;I validated these snippets by setting up a separate repo to test opening PRs. I also recommend using nektos/act as a CLI to validate your workflows if you want a more seamless dev experience.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/nektos" rel="noopener noreferrer"&gt;
        nektos
      &lt;/a&gt; / &lt;a href="https://github.com/nektos/act" rel="noopener noreferrer"&gt;
        act
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Run your GitHub Actions locally 🚀
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://raw.githubusercontent.com/wiki/nektos/act/img/logo-150.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fwiki%2Fnektos%2Fact%2Fimg%2Flogo-150.png" alt="act-logo"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Overview &lt;a href="https://github.com/nektos/act/actions" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/nektos/act/workflows/push/badge.svg?branch=master&amp;amp;event=push" alt="push"&gt;&lt;/a&gt; &lt;a href="https://gitter.im/nektos/act?utm_source=badge&amp;amp;utm_medium=badge&amp;amp;utm_campaign=pr-badge&amp;amp;utm_content=badge" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/47a8a6edfd3994df598bd7eb2da54f3e16894d1b6ca02ea1195e984179e79069/68747470733a2f2f6261646765732e6769747465722e696d2f6e656b746f732f6163742e737667" alt="Join the chat at https://gitter.im/nektos/act"&gt;&lt;/a&gt; &lt;a href="https://goreportcard.com/report/github.com/nektos/act" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/85c19cff19fdfc01ef44040d70d947337c6951389cf37b80ebffb57da41295fb/68747470733a2f2f676f7265706f7274636172642e636f6d2f62616467652f6769746875622e636f6d2f6e656b746f732f616374" alt="Go Report Card"&gt;&lt;/a&gt; &lt;a href="https://github.com/jonico/awesome-runners" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/7e9d420a1f5da7c99ba3ca9120d4c58bb231036904bcfd89dab61f6f5c4514b1/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c69737465642532306f6e2d617765736f6d652d2d72756e6e6572732d626c75652e737667" alt="awesome-runners"&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;"Think globally, &lt;code&gt;act&lt;/code&gt; locally"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Run your &lt;a href="https://developer.github.com/actions/" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt; locally! Why would you want to do this? Two reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fast Feedback&lt;/strong&gt; - Rather than having to commit/push every time you want to test out the changes you are making to your &lt;code&gt;.github/workflows/&lt;/code&gt; files (or for any changes to embedded GitHub actions), you can use &lt;code&gt;act&lt;/code&gt; to run the actions locally. The &lt;a href="https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables" rel="noopener noreferrer"&gt;environment variables&lt;/a&gt; and &lt;a href="https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners#filesystems-on-github-hosted-runners" rel="noopener noreferrer"&gt;filesystem&lt;/a&gt; are all configured to match what GitHub provides.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local Task Runner&lt;/strong&gt; - I love &lt;a href="https://en.wikipedia.org/wiki/Make_(software)" rel="nofollow noopener noreferrer"&gt;make&lt;/a&gt;. However, I also hate repeating myself. With &lt;code&gt;act&lt;/code&gt;, you can use the GitHub Actions defined in your &lt;code&gt;.github/workflows/&lt;/code&gt; to replace your &lt;code&gt;Makefile&lt;/code&gt;!&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;How Does It Work?&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;When you run &lt;code&gt;act&lt;/code&gt; it reads in your GitHub Actions from &lt;code&gt;.github/workflows/&lt;/code&gt; and determines the set of actions that need to be run. It uses the Docker API to either pull or build the necessary images, as defined in your workflow…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/nektos/act" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h1&gt;
  
  
  List of Snippets
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Post a comment automatically when a PR is opened&lt;/li&gt;
&lt;li&gt;Run a PHP snippet&lt;/li&gt;
&lt;li&gt;Open a Jira ticket when Dependabot opens a PR (for Hosted Jira instances)&lt;/li&gt;
&lt;li&gt;Update empty PR body with a picture of Barney&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Post a comment automatically when a PR is opened
&lt;/h1&gt;

&lt;p&gt;This snippet is more useful for understanding the structure of a GitHub Actions workflow than for actual production use. Please refer to the documentation for &lt;a href="https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/creating-a-pull-request-template-for-your-repository" rel="noopener noreferrer"&gt;creating a pull request template for your repository&lt;/a&gt; for a more robust alternative without using up your Actions quota.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add the below file to your repo as &lt;code&gt;.github/workflows/comment.yml&lt;/code&gt;&lt;/strong&gt;, or whatever other workflow name you find appropriate.&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;Comment&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;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&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;version_comment&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;Comment on a new PR with reminder&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;Add PR Comment&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;add_pr_comment&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;octokit/request-action@v2.x&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;route&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;POST /repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;Change&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;me&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;comment&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;you&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;want&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;automatically&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;put&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;🐶&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Run a PHP snippet
&lt;/h1&gt;

&lt;p&gt;For those of you (like myself) who work on teams developing primarily in PHP, it may be useful to also write your adhoc scripts in PHP for less context switching. TEST_ENV is placed here as an example environmental variable should anything about the PR's metadata need to be passed into the script.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add the below file to your repo as &lt;code&gt;.github/workflows/php.yml&lt;/code&gt;&lt;/strong&gt;, or whatever other workflow name you find appropriate.&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;Testing php scripts&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;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&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;version_comment&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;Comment on a new PR with reminder&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;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&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;Setup PHP&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;shivammathur/setup-php@v2&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;php-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;7.3'&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;Check PHP version&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;php --version&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;Run test.php&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;TEST_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;from&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ENV"&lt;/span&gt;
        &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;php scripts/test.php&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Add the below file to your repo as &lt;code&gt;scripts/test.php&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

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

&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Hello! This is scripts/test.php&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$github_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'GITHUB_TOKEN'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$test_env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'TEST_ENV'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$github_token&lt;/span&gt;&lt;span class="s2"&gt; = "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$github_token&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$test_env&lt;/span&gt;&lt;span class="s2"&gt; = "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$test_env&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&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;a&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Open a Jira ticket when Dependabot opens a PR (for Hosted Jira instances)
&lt;/h1&gt;

&lt;p&gt;The work for these files is that GitHub actions runs a Python script, and this script is the one responsible for firing off the Jira ticket's creation.&lt;/p&gt;

&lt;p&gt;This script will work for Jira instances that are both Cloud-based and Hosted. However, for those of you using Cloud-based instances, I encourage you to look into using the &lt;a href="https://github.com/marketplace/actions/jira-create-issue" rel="noopener noreferrer"&gt;Jira Create issue GitHub Action&lt;/a&gt; already provided by Atlassian. This will ensure you are using the latest and greatest. This feature is not available to those with Hosted instances.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before starting&lt;/strong&gt;, you will need to have a Jira account with a username and password that can be used on behalf of API calls. Naviate to &lt;a href="https://github.com/YOUR_USERNAME/YOUR_REPO/settings/secrets/actions" rel="noopener noreferrer"&gt;https://github.com/YOUR_USERNAME/YOUR_REPO/settings/secrets/actions&lt;/a&gt; and select "New repository secret". Save your Jira account's username as &lt;code&gt;JIRA_USER&lt;/code&gt; and your Jira account's password as &lt;code&gt;JIRA_PASS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add the below file to your repo as &lt;code&gt;.github/workflows/dependabot_to_jira.yml&lt;/code&gt;&lt;/strong&gt;, or whatever other workflow name you find appropriate.&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;Dependabot Jira Sync&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;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&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;run_jira_ticket&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;Create Jira Ticket for Dependabot PR&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;contains(github.event.pull_request.labels.*.name, 'Dependencies')&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;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&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;Set up Python&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/setup-python@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;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.x'&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;Install dependencies&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;python -m pip install --upgrade pip&lt;/span&gt;
        &lt;span class="s"&gt;pip install -r requirements.txt&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;Create Jira Ticket and Update PR Title&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;GITHUB_JSON&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ toJson(github) }}&lt;/span&gt;
        &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&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="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python3 scripts/jira_ticket.py&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Add the below file to your repo as &lt;code&gt;scripts/jira_ticket.py&lt;/code&gt;.&lt;/strong&gt;&lt;br&gt;
Make sure that you change jiraUrl to the url of your Jira hosted instance.&lt;/p&gt;

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

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="c1"&gt;# Get ENV variables.
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getENV&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="n"&gt;githubJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GITHUB_JSON&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;githubToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;github&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;githubJson&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;jiraUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;JIRA_USER&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;jiraPass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;JIRA_PASS&lt;/span&gt;&lt;span class="sh"&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;github&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;githubToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jiraUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jiraPass&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;createJiraTicket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jiraUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jiraPass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;githubPRTitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;githubPRURL&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;jiraUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://backlog.YOURINSTANCE.com/rest/api/2/issue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
  &lt;span class="n"&gt;jiraHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;jiraBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fields&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;project&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EEE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;summary&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;githubPRTitle&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;githubPRURL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;jiraUser&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;issuetype&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Ticket&lt;/span&gt;&lt;span class="sh"&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;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jiraUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jiraUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jiraPass&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;jiraHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;jiraBody&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;jiraResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&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;jiraResponse&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Make API call to GitHub to change PR title.
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;updateGithubPRTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;githubToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jiraKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;githubPRTitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;githubRepo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;githubPRNum&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;githubHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;token &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;githubToken&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;githubBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;jiraKey&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;githubPRTitle&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;githubUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.github.com/repos/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;githubRepo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/pulls/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;githubPRNum&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
  &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;githubUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;githubBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;githubHeaders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;githubToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jiraUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jiraPass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getENV&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="n"&gt;githubRepo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;repository&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;githubPRURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pull_request&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;_links&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;html&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;href&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;githubPRTitle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pull_request&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;githubPRNum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pull_request&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;number&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="n"&gt;jiraKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createJiraTicket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jiraUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jiraPass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;githubPRTitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;githubPRURL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jiraKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;updateGithubPRTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;githubToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jiraKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;githubPRTitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;githubRepo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;githubPRNum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Update empty PR body with a picture of Barney
&lt;/h1&gt;

&lt;p&gt;One of my coworkers used to do this whenever a PR was opened without a body for a significant amount of time. This is a fun GitHub action the team implemented together to learn more about the GitHub action development process.&lt;/p&gt;

&lt;p&gt;This snippet is different from the others in that we constructed our own connector and stored it in the repo with the workflow's file. Alternatively, if you wish for the work to be more widely available, you would keep the repo dedicated to this singular action and publish it to the &lt;a href="https://github.com/marketplace?type=actions" rel="noopener noreferrer"&gt;Marketplace&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add the below file to your repo as &lt;code&gt;.github/workflows/barney.yml&lt;/code&gt;&lt;/strong&gt;, or whatever other workflow name you find appropriate.&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;Body by Barney&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;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&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;barney_job&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Barney is here&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# To use this repository's private action, you must check out the repository&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@v2&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;I love you&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;./&lt;/span&gt; &lt;span class="c1"&gt;# Uses an action in the root directory&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;barney&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;barney.yml&lt;/code&gt; points to the root of the repository for where the action is located, specifically in this case an &lt;code&gt;action.yml&lt;/code&gt; file. &lt;strong&gt;Add the below file to your repo as &lt;code&gt;action.yml&lt;/code&gt;.&lt;/strong&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Barney'&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;Barney'&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;docker'&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Dockerfile'&lt;/span&gt;
&lt;span class="na"&gt;branding&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;heart&lt;/span&gt;
  &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;red&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;action.yml&lt;/code&gt; then points to a Dockerfile, also located to the root of your repo. &lt;strong&gt;Add the below file to your repo as &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

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

&lt;span class="n"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;slim&lt;/span&gt;

&lt;span class="c1"&gt;# A bunch of `LABEL` fields for GitHub to index
&lt;/span&gt;&lt;span class="n"&gt;LABEL&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;com.github.actions.name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Body by Barney&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;LABEL&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;com.github.actions.description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Auto-populate empty PR descriptions.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;LABEL&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;com.github.actions.icon&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;edit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;LABEL&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;com.github.actions.color&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;purple&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;LABEL&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;repository&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://github.com/YOUR_USERNAME/YOUR_REPO&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;LABEL&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;homepage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://github.com/YOUR_USERNAME/YOUR_REPO&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;LABEL&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maintainer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR NAME (https://github.com/YOUR_USERNAME)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# install
&lt;/span&gt;&lt;span class="n"&gt;COPY&lt;/span&gt; &lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="n"&gt;RUN&lt;/span&gt; &lt;span class="n"&gt;npm&lt;/span&gt; &lt;span class="n"&gt;ci&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;production&lt;/span&gt;

&lt;span class="c1"&gt;# start
&lt;/span&gt;&lt;span class="n"&gt;COPY&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;ENTRYPOINT&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;node&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/index.js&lt;/span&gt;&lt;span class="sh"&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;code&gt;Dockerfile&lt;/code&gt; then points to &lt;code&gt;/index.js&lt;/code&gt; for where the remainder of this action fires off, another file in the root. &lt;strong&gt;Add the below file to your repo as &lt;code&gt;index.js&lt;/code&gt;.&lt;/strong&gt; The action here will comment in logs whether Barney is applied or needed on newly opened PRs.&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Toolkit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;actions-toolkit&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;getPR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./lib/get-pr&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;updatePR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./lib/update-pr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;Toolkit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;tools&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;pr_result&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;getPR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PR_BODY=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;pr_result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&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;pr_result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;updatePR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Barney has been applied.&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No Barney needed.&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="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&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;pull_request.opened&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="p"&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;GITHUB_TOKEN&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;index.js&lt;/code&gt; now looks to a folder &lt;code&gt;lib/&lt;/code&gt; where the rest of the files are located. Instead of storing the full implementation in one file, we still make use of separating the components of this action into parts - in the event the code needs to be revisited and refactored.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;lib/&lt;/code&gt; contains two files: &lt;code&gt;get-pr.js&lt;/code&gt; for getting information about the PR and &lt;code&gt;update-pr.js&lt;/code&gt; for sending an API call with the image of Barney in the description.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add the below file to your repo as &lt;code&gt;get-pr.js&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

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

&lt;span class="cm"&gt;/**
 * Return the PR.
 * @param {import('actions-toolkit').Toolkit} tools
 */&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getPR&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tools&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;tools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pulls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;tools&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;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;pull_number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tools&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;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pull_request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;number&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;p&gt;&lt;strong&gt;Add the below file to your repo as &lt;code&gt;update-pr.js&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

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

&lt;span class="cm"&gt;/**
 * Update the PR body with Barney.
 * @param {import('actions-toolkit').Toolkit} tools
 */&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updatePR&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tools&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;barney_img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://user-images.githubusercontent.com/1390106/66919899-3a406900-eff0-11e9-83ba-4e6c4c3dbfaf.jpg&lt;/span&gt;&lt;span class="dl"&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;tools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pulls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;tools&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;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;pull_number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tools&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;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pull_request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;![image](&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;barney_img&lt;/span&gt; &lt;span class="o"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And there you have it! You can expand &lt;code&gt;update-pr.js&lt;/code&gt; with whatever content you find appropriate. This snippet will specifically update an empty PR body with the below image:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F1390106%2F66919899-3a406900-eff0-11e9-83ba-4e6c4c3dbfaf.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%2Fuser-images.githubusercontent.com%2F1390106%2F66919899-3a406900-eff0-11e9-83ba-4e6c4c3dbfaf.jpg" alt="Barney the Dinosaur"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Using Slack for phone two-factor-authentication with help from Twilio</title>
      <dc:creator>Kathryn DiPippo</dc:creator>
      <pubDate>Fri, 07 May 2021 18:14:42 +0000</pubDate>
      <link>https://dev.to/kdipippo/using-slack-for-phone-two-factor-authentication-with-help-from-twilio-40eb</link>
      <guid>https://dev.to/kdipippo/using-slack-for-phone-two-factor-authentication-with-help-from-twilio-40eb</guid>
      <description>&lt;p&gt;For several months at my position, I worked with an online platform that solely utilized text messages for two-factor authentication (otherwise abbreviated 2FA). Security is vital, but security can also be increasingly annoying when the website forces logouts every 15 minutes or you frequently change location around the office and prefer to leave your phone locked in your desk.&lt;/p&gt;

&lt;p&gt;The intent of this workaround is not to reduce security but to make it more personally convenient and discourage disabling the security entirely. I may not have my phone on me, but I'm definitely with my work laptop with Slack open.&lt;/p&gt;

&lt;h2&gt;
  
  
  The High-Level Pipeline
&lt;/h2&gt;

&lt;p&gt;This procedure assumes two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The platform you want to use this procedure against allows for simple phone number TFA. You would receive a text message with a number or random character string code that you would type into the verification entry.&lt;/li&gt;
&lt;li&gt;The platform allows Twilio-type numbers. This is not the case with every platform that uses phone number 2FA! Your mileage may vary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The high-level steps that will be broken down in this walkthrough:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Obtain a Twilio phone number&lt;/li&gt;
&lt;li&gt;Create webhook listener in Slack&lt;/li&gt;
&lt;li&gt;Create phone webhook in Twilio&lt;/li&gt;
&lt;li&gt;Update your 2FA phone number on the web platform with the Twilio phone number&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will be updating the 2FA phone number with the Twilio number last - in the event the platform requires a confirmation text message immediately upon changing. However, it may be useful to perform this step earlier in case there is a restriction against programmable Twilio-based phone numbers are mentioned in the earlier bullet points.&lt;/p&gt;

&lt;h2&gt;
  
  
  Obtain a Twilio Phone Number
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.twilio.com/" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt; is a cloud communications platform as a service (CPaaS) that allows software developers to programmatically make and receive phone calls, send and receive text messages, and perform other communication functions using its web service APIs. We will be using it as the pipeline connecting your platform to Slack.&lt;/p&gt;

&lt;p&gt;When you create a Twilio account, you'll be given a single Twilio phone number with a limited amount of trial usage. However, despite how frequently I was logging into the platform I set this up for, it took over a year for the base free trial limits to be utilized - and after that, a single $15 payment has lasted far beyond that.&lt;/p&gt;

&lt;p&gt;Additional phone numbers on this account may require additional purchases.&lt;/p&gt;

&lt;p&gt;By the end of this step, you will now have a Twilio phone number complete with an area code. Copy this for later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create webhook listener in Slack
&lt;/h2&gt;

&lt;p&gt;Navigate back to your Slack instance. Please check out &lt;a href="https://api.slack.com/messaging/webhooks" rel="noopener noreferrer"&gt;Slack's walkthrough for setting up an incoming webhook&lt;/a&gt; before continuing. The page will guide you with how to make a Slack App and the permissions necessary. I recommend setting the webhook's destination to your own private DMs as opposed to a channel.&lt;/p&gt;

&lt;p&gt;By the end of this step, you will now also have a Slack webhook URL formatted similarly to &lt;code&gt;https://hooks.slack.com/services/???/???/???&lt;/code&gt;. Copy this for later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create phone webhook in Twilio Studio
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.twilio.com/console/studio/dashboard" rel="noopener noreferrer"&gt;Twilio Studio&lt;/a&gt; is a visual editor for building communications workflows. We will be using this as the brain to take messages sent to our Twilio phone number to a webhook. This step is performed before you change your phone number in the event the platform will send a confirmation text after applying the switch.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;While logged into the Twilio platform, navigate to &lt;a href="https://www.twilio.com/console/studio/dashboard" rel="noopener noreferrer"&gt;https://www.twilio.com/console/studio/dashboard&lt;/a&gt;. You can also navigate to Studio by selecting the icon with 3 dots on the left menu and select "Studio" that appears under the "Runtime" subheading.&lt;/li&gt;
&lt;li&gt;Under "Recent Flows" select the button to "Create new flow".&lt;/li&gt;
&lt;li&gt;For "Flow Name", I named my own flow "SMS Notification", but feel free to name it something with greater identification for its purpose. Select "Next".&lt;/li&gt;
&lt;li&gt;Scroll down in the available templates to the option "Import from JSON". Select this box and then select "Next".&lt;/li&gt;
&lt;li&gt;Copy the below JSON snippet into the text box that appears on this screen. Before continuing, change the value on line 45 for the webhook url from the placeholder I've put to the URL you generated as part of the second step.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A New Flow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"states"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Trigger"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"trigger"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"transitions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"post_to_slack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"incomingMessage"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"incomingCall"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"incomingRequest"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"offset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"y"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"post_to_slack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"make-http-request"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"transitions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"failed"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"offset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"y"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"content_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/json;charset=utf-8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;{{trigger.message.Body}}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://hooks.slack.com/services/???/???/???"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"initial_state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Trigger"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"flags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allow_concurrent_calls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;p&gt;Clicking next will create the below end result:&lt;br&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%2Fbz937r5gkb3gshovadoz.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%2Fbz937r5gkb3gshovadoz.png" alt="Screen Shot 2021-05-07 at 1.37.31 PM"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This studio flow is its own webhook to listen for SMS triggers and forward the message to a Slack webhook. To connect this implementation to your phone number:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go back to the left menu and select "Phone Numbers" under the "Super Network" subheading.&lt;/li&gt;
&lt;li&gt;Click on the phone number you are going to use for this Platform -&amp;gt; Twilio -&amp;gt; Slack pipeline.&lt;/li&gt;
&lt;li&gt;Scroll to the bottom of the "Configure" page to the "Messaging " section.

&lt;ul&gt;
&lt;li&gt;"Configure With" should be set to "Webhooks, TwiML Bins, Functions, Studio, or Proxy"&lt;/li&gt;
&lt;li&gt;Change the "A Message Comes In" dropdown to "Studio Flow"&lt;/li&gt;
&lt;li&gt;Change the dropdown directly to the right to the name of the studio flow you created just earlier.&lt;/li&gt;
&lt;li&gt;Select "Save" at the bottom of the page.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;You have now finished setting up Twilio for this phone number to trigger a Twilio-facing webhook to fire off a Slack webhook.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update your 2FA phone number on the web platform with the Twilio phone number
&lt;/h2&gt;

&lt;p&gt;Last but not least, update the platform with your new phone number. You should now be seeing a Slack message pop in every time you need to perform 2FA and enter an SMS code.&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%2Ff2ujm6m27um9rbpxmzb2.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%2Ff2ujm6m27um9rbpxmzb2.png" alt="Screen Shot 2021-05-07 at 2.10.42 PM"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hope this tutorial was helpful! Feel free to comment with any questions or updates to this procedure.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Submit Your Hacktoberfest Repos to up-for-grabs!</title>
      <dc:creator>Kathryn DiPippo</dc:creator>
      <pubDate>Sun, 27 Sep 2020 00:25:39 +0000</pubDate>
      <link>https://dev.to/kdipippo/submit-your-hacktoberfest-repos-to-up-for-grabs-1a3</link>
      <guid>https://dev.to/kdipippo/submit-your-hacktoberfest-repos-to-up-for-grabs-1a3</guid>
      <description>&lt;p&gt;As &lt;a href="https://hacktoberfest.digitalocean.com/"&gt;Hacktoberfest 2020&lt;/a&gt; descends in less than a week (!!!), I wanted to give a shoutout to &lt;a href="https://up-for-grabs.net/"&gt;up-for-grabs.net&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/up-for-grabs"&gt;
        up-for-grabs
      &lt;/a&gt; / &lt;a href="https://github.com/up-for-grabs/up-for-grabs.net"&gt;
        up-for-grabs.net
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      This is a list of projects which have curated tasks specifically for new contributors. These issues are a great way to get started with a project, or to help share the load of working on open source projects. Jump in!
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;
&lt;a href="https://up-for-grabs.net/" rel="nofollow"&gt;up-for-grabs.net&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/up-for-grabs/up-for-grabs.net/workflows/Continuous%20Integration/badge.svg"&gt;&lt;img src="https://github.com/up-for-grabs/up-for-grabs.net/workflows/Continuous%20Integration/badge.svg" alt="Continuous Integration status"&gt;&lt;/a&gt;
&lt;a href="https://app.netlify.com/sites/up-for-grabs-test-bench/deploys" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/824997b7479ad92400a3cec0d1d5321f8e7064d5f7c43aace0aea87d7a5e7ca8/68747470733a2f2f6170692e6e65746c6966792e636f6d2f6170692f76312f6261646765732f30656537626639662d316165642d343635622d386539652d3031646530303961386137652f6465706c6f792d737461747573" alt="Netlify Status"&gt;&lt;/a&gt;
&lt;a href="https://github.com/up-for-grabs/up-for-grabs.net#contributors"&gt;&lt;img src="https://camo.githubusercontent.com/fa53fa893b6123dfa169615207b12e144b000e3f1d442300707695eb62f41b72/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f616c6c5f636f6e7472696275746f72732d31312d6f72616e67652e7376673f7374796c653d666c61742d737175617265" alt="All Contributors"&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://up-for-grabs.net/" rel="nofollow"&gt;&lt;img width="814" src="https://res.cloudinary.com/practicaldev/image/fetch/s--w0BrCouB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/359239/67390578-50f83a00-f573-11e9-835d-02eb9e019afb.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This repository contains the content for the &lt;a href="https://up-for-grabs.net/" rel="nofollow"&gt;Up-For-Grabs website&lt;/a&gt;, a list of projects with curated tasks for new contributors.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;List your project&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;If you know of or own a project that should be listed on Up for Grabs
&lt;a href="https://github.com/up-for-grabs/up-for-grabs.netdocs/list-a-project.md"&gt;follow the instructions to open a pull request&lt;/a&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Contributing&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;If you would like to get involved with the project, please read the
&lt;a href="https://github.com/up-for-grabs/up-for-grabs.net.github/CONTRIBUTING.md"&gt;CONTRIBUTING.md&lt;/a&gt; file as it contains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Instructions about getting your environment setup&lt;/li&gt;
&lt;li&gt;Guidance for testing locally&lt;/li&gt;
&lt;li&gt;Commands to run to verify changes&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How does the site work?&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;If you want to learn more about the various parts of the Up-for-Grabs project,
the &lt;a href="https://github.com/up-for-grabs/up-for-grabs.netdocs/how-it-works.md"&gt;How it works&lt;/a&gt; document is a good overview.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Contributors ✨&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Thanks to these wonderful people who have improved the code and documentation to help this project grow. (&lt;a href="https://allcontributors.org/docs/en/emoji-key" rel="nofollow"&gt;emoji key&lt;/a&gt;):&lt;/p&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;
&lt;a href="https://github.com/filipe-gomes"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ofipwSlz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://avatars1.githubusercontent.com/u/42053052%3Fv%3D4%3Fs%3D100" width="100px;" alt="Filipe Magalhaes Gomes"&gt;&lt;br&gt;&lt;b&gt;Filipe Magalhaes Gomes&lt;/b&gt;&lt;/a&gt;&lt;br&gt;&lt;a href="https://github.com/up-for-grabs/up-for-grabs.net#content-filipe-gomes" title="Content"&gt;🖋&lt;/a&gt;
&lt;/td&gt;
      &lt;td&gt;
&lt;a href="https://mkkhedawat.github.io/" rel="nofollow"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ij1ig7e7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://avatars2.githubusercontent.com/u/5137374%3Fv%3D4%3Fs%3D100" width="100px;" alt="Manish Kumar Khedawat"&gt;&lt;br&gt;&lt;b&gt;Manish Kumar Khedawat&lt;/b&gt;&lt;/a&gt;&lt;br&gt;&lt;a href="https://github.com/up-for-grabs/up-for-grabs.net/commits?author=mkkhedawat" title="Code"&gt;💻&lt;/a&gt;
&lt;/td&gt;
      &lt;td&gt;
&lt;a href="https://github.com/SpaceEEC"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AUp9aIwY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://avatars1.githubusercontent.com/u/24881032%3Fv%3D4%3Fs%3D100" width="100px;" alt="SpaceEEC"&gt;&lt;br&gt;&lt;b&gt;SpaceEEC&lt;/b&gt;&lt;/a&gt;&lt;br&gt;&lt;a href="https://github.com/up-for-grabs/up-for-grabs.net/commits?author=SpaceEEC" title="Code"&gt;💻&lt;/a&gt;
&lt;/td&gt;
      &lt;td&gt;
&lt;a href="http://www.joaomanoel.com.br" rel="nofollow"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--55nusBUc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://avatars0.githubusercontent.com/u/6238111%3Fv%3D4%3Fs%3D100" width="100px;" alt="João Manoel Lins"&gt;&lt;br&gt;&lt;b&gt;João Manoel Lins&lt;/b&gt;&lt;/a&gt;&lt;br&gt;&lt;a href="https://github.com/up-for-grabs/up-for-grabs.net#content-JoaoManoel" title="Content"&gt;🖋&lt;/a&gt;
&lt;/td&gt;
      &lt;td&gt;
&lt;a href="https://github.com/crystal-dawn"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jyVl4sLl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://avatars3.githubusercontent.com/u/38540136%3Fv%3D4%3Fs%3D100" width="100px;" alt="Crystal Yungwirth"&gt;&lt;br&gt;&lt;b&gt;Crystal&lt;/b&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;…&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/up-for-grabs/up-for-grabs.net"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;I find it a super useful resource when looking for OSS to contribute to with my current skillset. I'd love for others who are prepping their projects for Hacktoberfest to make use of it - or those who are also exploring what's out there.&lt;/p&gt;

&lt;p&gt;If you are prepping your repos for Hacktoberfest, you can use the Hacktoberfest label as the "beginner-friendly" tag when submitting. &lt;a href="https://www.openfaas.com/"&gt;OpenFaaS&lt;/a&gt;, an open source implementation of Function as a Service, and &lt;a href="https://matomo.org/"&gt;Matomo&lt;/a&gt;, "the leading open alternative to Google Analytic", both use their "Hacktoberfest" issue labels in this manner, for example.&lt;/p&gt;

&lt;p&gt;Hope you find this resource as helpful as I do. I'm more than happy to assist with any PRs if you want your repo added to the listing. Good luck during Hacktoberfest!&lt;/p&gt;

</description>
      <category>hacktoberfest</category>
      <category>github</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Rendering OpenAPI specs in Angular</title>
      <dc:creator>Kathryn DiPippo</dc:creator>
      <pubDate>Sat, 19 Sep 2020 04:17:33 +0000</pubDate>
      <link>https://dev.to/kdipippo/rendering-openapi-specs-in-angular-582f</link>
      <guid>https://dev.to/kdipippo/rendering-openapi-specs-in-angular-582f</guid>
      <description>&lt;p&gt;APIs and API documentation go hand-in-hand. With the help of of the &lt;code&gt;swagger-ui-dist&lt;/code&gt; npm package, it's super easy to take an OAS YAML or JSON file and display it as a separate page for others to browse. This walkthrough will create an Angular component dedicated to showing the API documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assemble your OAS spec
&lt;/h2&gt;

&lt;p&gt;Both JSON and YAML can be used for this process. The main key is that the file needs to be accessible via a live URL. You can do this by taking your spec and pushing it to a public repo and utlilizing GitHub's "Raw" file feature.&lt;/p&gt;

&lt;p&gt;I will be using the files &lt;a href="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v3.0/petstore.yaml" rel="noopener noreferrer"&gt;https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v3.0/petstore.yaml&lt;/a&gt; found in the &lt;code&gt;examples/&lt;/code&gt; folder for OAS v3.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/OAI" rel="noopener noreferrer"&gt;
        OAI
      &lt;/a&gt; / &lt;a href="https://github.com/OAI/OpenAPI-Specification" rel="noopener noreferrer"&gt;
        OpenAPI-Specification
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      The OpenAPI Specification Repository
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Add swagger-ui-dist to package.json and angular.json files
&lt;/h2&gt;

&lt;p&gt;In the root of your project, run:&lt;/p&gt;

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

$ npm install swagger-ui-dist


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

&lt;/div&gt;

&lt;p&gt;This will add the &lt;code&gt;swagger-ui-dist&lt;/code&gt; CSS and JS files needed to render the API documentation layout.&lt;/p&gt;

&lt;p&gt;You'll next need to include said files into the "styles" and "scripts" section of your Angular project for them to be pulled in. See the lines below marked with +s for what to add to the project build:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"architect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"builder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@angular-devkit/build-angular:browser"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"outputPath"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/example-angular-project"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/index.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/main.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"polyfills"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/polyfills.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"tsConfig"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsconfig.app.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"aot"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"assets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"src/favicon.ico"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"src/assets"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"styles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="s2"&gt;"node_modules/swagger-ui-dist/swagger-ui.css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"src/styles.css"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="s2"&gt;"node_modules/swagger-ui-dist/swagger-ui-bundle.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="s2"&gt;"node_modules/swagger-ui-dist/swagger-ui-standalone-preset.js"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Create a new Angular component
&lt;/h2&gt;

&lt;p&gt;Continue as you would with adding an Angular component.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ ng g c api


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

&lt;/div&gt;

&lt;p&gt;Update &lt;code&gt;app-routing.module.ts&lt;/code&gt; to route &lt;code&gt;https://localhost:4200/api&lt;/code&gt; to this component.&lt;/p&gt;

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

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NgModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RouterModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HomeComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./home/home.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApiComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./api/api.component&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;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HomeComponent&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApiComponent&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="nd"&gt;NgModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;RouterModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
  &lt;span class="na"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;RouterModule&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppRoutingModule&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;Spin up the project using &lt;code&gt;ng serve&lt;/code&gt; to confirm that &lt;code&gt;https://localhost:4200/api&lt;/code&gt; shows you the generated &lt;code&gt;&amp;lt;p&amp;gt;api works!&amp;lt;/p&amp;gt;&lt;/code&gt; message.&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%2Fi%2Fef0cochro1q5anz045cu.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%2Fi%2Fef0cochro1q5anz045cu.png" alt="Expected successful Angular component integration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating ApiComponent to utilize swagger-ui-dist
&lt;/h2&gt;

&lt;p&gt;Change the &lt;code&gt;api.component.html&lt;/code&gt; file to include the below div. The &lt;code&gt;swagger-ui&lt;/code&gt; ID will be what the OpenAPI spec display gets rendered inside.&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"swagger-ui"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Meanwhile, &lt;code&gt;api.component.ts&lt;/code&gt; should be updated with the following:&lt;/p&gt;

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

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SwaggerUIBundle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SwaggerUIStandalonePreset&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;swagger-ui-dist&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="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./api.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&lt;/span&gt;&lt;span class="p"&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;./api.component.css&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApiComponent&lt;/span&gt; &lt;span class="kr"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&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="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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;ui&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SwaggerUIBundle&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;dom_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#swagger-ui&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BaseLayout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;presets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;SwaggerUIBundle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;presets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;SwaggerUIBundle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SwaggerUIStandalonePreset&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/yaml/petstore.yaml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;operationsSorter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alpha&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="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Going through the notable changes, we first import SwaggerUIBundle and SwaggerUIStandalonePreset from the &lt;code&gt;swagger-ui-dist&lt;/code&gt; node package added earlier. This will allow us to initial the SwaggerUIBundle() call that gets made when the component initializes.&lt;/p&gt;

&lt;p&gt;We can see that &lt;code&gt;swagger-ui&lt;/code&gt; ID mentioned prior. This can be changed if needed (i.e. if you want multiple specs to show on the same page and need to distinguish between the two). For more information about these settings and ways to customize them, the SwaggerUI documentation can be found at &lt;a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/" rel="noopener noreferrer"&gt;https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Most notably, the &lt;code&gt;url&lt;/code&gt; is just set to the &lt;code&gt;raw.githubusercontent.com&lt;/code&gt; URL for the OpenAPI spec's YAML file.&lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;ng serve&lt;/code&gt; on the same page, and you should now see your API documentation nicely rendered!&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%2Fi%2Fm3bhwq4276t9mwbrv4ge.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%2Fi%2Fm3bhwq4276t9mwbrv4ge.png" alt="Expected successful OpenAPI spec in Angular"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;With just a few steps, we are now able to house a dynamic view of the API documentation in our Angular project. I recommend using this method to host your documentation with GitHub pages while also providing users to download the spec raw.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>openapi</category>
      <category>documentation</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
