<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Dragoș Străinu</title>
    <description>The latest articles on Forem by Dragoș Străinu (@strdr4605).</description>
    <link>https://forem.com/strdr4605</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%2F161616%2F7b63f9f2-cf3f-48d9-96a7-1b34f12f97d2.jpeg</url>
      <title>Forem: Dragoș Străinu</title>
      <link>https://forem.com/strdr4605</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/strdr4605"/>
    <language>en</language>
    <item>
      <title>MacOS: notify when the terminal command is finished</title>
      <dc:creator>Dragoș Străinu</dc:creator>
      <pubDate>Fri, 21 Apr 2023 09:04:17 +0000</pubDate>
      <link>https://forem.com/strdr4605/macos-notify-when-the-terminal-command-is-finished-4ji6</link>
      <guid>https://forem.com/strdr4605/macos-notify-when-the-terminal-command-is-finished-4ji6</guid>
      <description>&lt;p&gt;After reading &lt;a href="https://evanhahn.com/a-decade-of-dotfiles/"&gt;A decade of dotfiles&lt;/a&gt; I wanted to create a similar bash function to &lt;code&gt;boop&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And here it is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ~/.zshrc&lt;/span&gt;
&lt;span class="c"&gt;# ...rest of zshrc&lt;/span&gt;
,notify &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;last_exit_status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$last_exit_status&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'0'&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;osascript &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"display notification &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Done&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; with title &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Good&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; sound name &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Fonk&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;else
    &lt;/span&gt;osascript &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"display notification &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Exit code: &lt;/span&gt;&lt;span class="nv"&gt;$last_exit_status&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; with title &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Bad&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; sound name &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Ping&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;
  &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$last_exit_status&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It uses &lt;a href="https://ss64.com/osx/osascript.html"&gt;osascript&lt;/a&gt; to execute the AppleScripts that pushes the notification.&lt;br&gt;&lt;br&gt;
According to &lt;a href="https://stackoverflow.com/questions/67406491/osascript-how-to-pass-in-a-variable/67413043#67413043"&gt;stackoverflow&lt;/a&gt; &lt;br&gt;
this is not the best way to pass bash variable to osascript, &lt;br&gt;
but I did not understand how to do it for my use case and as I use &lt;code&gt;last_exit_status&lt;/code&gt; there should be no problem.&lt;/p&gt;

&lt;p&gt;usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run &lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;,notify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I prefix the function name with a comma to faster search for it when doing &lt;code&gt;;,&amp;lt;Tab&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can test it with &lt;code&gt;(exit 0);,notify&lt;/code&gt; and &lt;code&gt;(exit 1);,notify&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TmG_eIxA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strdr4605.com/notify-good.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TmG_eIxA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strdr4605.com/notify-good.png" alt="notify good" width="722" height="148"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i_lUBQ1---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strdr4605.com/notify-bad.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i_lUBQ1---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://strdr4605.com/notify-bad.png" alt="notify bad" width="732" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>macos</category>
      <category>bash</category>
      <category>zsh</category>
    </item>
    <item>
      <title>Stop doing git checkout master branch</title>
      <dc:creator>Dragoș Străinu</dc:creator>
      <pubDate>Tue, 14 Mar 2023 13:19:58 +0000</pubDate>
      <link>https://forem.com/strdr4605/stop-doing-git-checkout-master-branch-55bi</link>
      <guid>https://forem.com/strdr4605/stop-doing-git-checkout-master-branch-55bi</guid>
      <description>&lt;p&gt;I see a common workflow when starting to work on new task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout master
git pull &lt;span class="c"&gt;# or git pull -r&lt;/span&gt;
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; feat/4605-new-task
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But I do things a bit differently. The main difference is that I don't care about the local &lt;code&gt;master&lt;/code&gt; branch because the source of truth is always remote &lt;code&gt;origin/master&lt;/code&gt;. &lt;br&gt;
I don't do commits on &lt;code&gt;master&lt;/code&gt; branch, because all changes to the project are done through Pull Requests. So why should I keep the local &lt;code&gt;master&lt;/code&gt; in sync with &lt;code&gt;origin/master&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;The first thing I do is fetch the remote master branch to get the latest commits.&lt;/p&gt;

&lt;p&gt;I have an alias in my &lt;code&gt;.gitconfig&lt;/code&gt; file:&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="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  fetch-and-clean-branches &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"!git fetch -p &amp;amp;&amp;amp; git branch -vv | grep ': gone]'|  grep -v "&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;" | awk '{ print &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;; }' | xargs git branch -D"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://morgan.cugerone.com/blog/quick-tip-how-to-locally-delete-all-remotely-merged-git-branches/"&gt;How to locally delete all remotely merged git branches&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Running &lt;code&gt;git fetch-and-clean-branches&lt;/code&gt;, will fetch new updates on remote branches and will remove local branches that are not relevant anymore.&lt;/p&gt;

&lt;p&gt;Next, I create a new branch based on &lt;code&gt;origin/master&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; feat/4605-new-task origin/master &lt;span class="c"&gt;# --no-track&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also created an alias for this command:&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="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  fetch-and-clean-branches &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"!git fetch -p &amp;amp;&amp;amp; git branch -vv | grep ': gone]'|  grep -v "&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;" | awk '{ print &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;; }' | xargs git branch -D"&lt;/span&gt;
  start &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"!git fetch-and-clean-branches &amp;amp;&amp;amp; git checkout -b &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; --no-track origin/master #"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And I can use it as: &lt;code&gt;git start feat/4605-new-task&lt;/code&gt;. &lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Related: &lt;a href="//https:strdr4605.com/how-i-use-git"&gt;How I use Git&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>git</category>
    </item>
    <item>
      <title>How to gradually add an eslint rule</title>
      <dc:creator>Dragoș Străinu</dc:creator>
      <pubDate>Wed, 18 May 2022 09:51:38 +0000</pubDate>
      <link>https://forem.com/strdr4605/how-to-gradually-add-an-eslint-rule-1ced</link>
      <guid>https://forem.com/strdr4605/how-to-gradually-add-an-eslint-rule-1ced</guid>
      <description>&lt;p&gt;As your javascript project grows and new engineers are joining the team, you may try to add new eslint plugins and rules to make the code base more rigid.&lt;/p&gt;

&lt;p&gt;The problem is that often these new eslint rules may require changes in all project files, and as your project is big enough, you may have a situation when eslint rules require changes in &lt;a href="https://strdr4605.com/how-i-found-a-bug-in-2000-files"&gt;2000+ files&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What if you could gradually enforce an eslint rule, without breaking the CI of your project.&lt;/p&gt;

&lt;p&gt;We can do this using &lt;a href="https://github.com/okonet/lint-staged"&gt;lint-staged&lt;/a&gt;, &lt;a href="https://typicode.github.io/husky/"&gt;husky&lt;/a&gt;, and some changes in the &lt;code&gt;.eslintrc.js&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;I hope you are already using &lt;code&gt;lint-staged&lt;/code&gt; with &lt;code&gt;husky&lt;/code&gt;. If not, please set up it.&lt;/p&gt;

&lt;p&gt;Your &lt;code&gt;.lintstagedrc.js&lt;/code&gt; file may look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// .lintstagedrc.js&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="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*.{js,jsx,ts,tsx}&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prettier --write&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;eslint --quiet --fix&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stylelint --fix&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prettier --write&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may also have a script in your &lt;code&gt;package.json&lt;/code&gt; file, that you run in CI to make sure eslint rules are followed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&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="nl"&gt;"eslint:run"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eslint --ext=.js,.jsx,.ts,.tsx --quiet ."&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;Now let's say you want to enforce the removal of all console logs using the eslint rule &lt;code&gt;no-console&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In your &lt;code&gt;.eslintrc.js&lt;/code&gt; file, set the rule to warning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// .eslintrc.js&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="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;rules&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;no-console&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;warn&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;Now running &lt;code&gt;npm run eslint:run&lt;/code&gt; will just show a bunch of warnings but will pass the check.&lt;br&gt;
Next, you need to create a new eslintrc file that will be used only by &lt;code&gt;lint-staged&lt;/code&gt;. Let's name it &lt;code&gt;eslintrc-staged.js&lt;/code&gt;. &lt;br&gt;
There you need to extend the default eslint config and override the rule to error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// .eslintrc-staged.js&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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./.eslintrc.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;rules&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;no-console&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&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;The last change needs to be done in &lt;code&gt;.lintstagedrc.js&lt;/code&gt; to tell lint-staged to use another config file when running eslint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="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="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*.{js,jsx,ts,tsx}&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prettier --write&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;eslint -c eslintrc-staged.js --no-eslintrc --quiet --fix&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stylelint --fix&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prettier --write&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when doing changes to the project, eslint will throw errors only to the files that were changed before the commit.&lt;/p&gt;

</description>
      <category>development</category>
      <category>eslint</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>How I found a bug in 2000 files</title>
      <dc:creator>Dragoș Străinu</dc:creator>
      <pubDate>Sun, 15 May 2022 09:28:22 +0000</pubDate>
      <link>https://forem.com/strdr4605/how-i-found-a-bug-in-2000-files-34h0</link>
      <guid>https://forem.com/strdr4605/how-i-found-a-bug-in-2000-files-34h0</guid>
      <description>&lt;p&gt;I was adding a new eslint plugin that will sort imports in js/ts files.&lt;br&gt;&lt;br&gt;
After adding &lt;a href="https://github.com/lydell/eslint-plugin-simple-import-sort"&gt;eslint-plugin-simple-import-sort&lt;/a&gt;, &lt;br&gt;
I run &lt;code&gt;eslint --fix&lt;/code&gt; and found about 2000 files changed. &lt;br&gt;
As there was only reorder of imports I thought that there should not be any issues.&lt;/p&gt;

&lt;p&gt;I was surprised when I found a very strange&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TypeError: Cannot read properties of undefined
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reverting the import order did not help to fix the issue.&lt;br&gt;&lt;br&gt;
And now I started thinking  about how I can find the issue without going through all 2000 files. &lt;/p&gt;

&lt;p&gt;Then an idea came. As all changes are independent,&lt;br&gt;
I could do a commit for every file change and then run a &lt;code&gt;git bisect&lt;/code&gt; and find the file that causes the problem.&lt;/p&gt;

&lt;p&gt;So I created a bash script that will get all file names from &lt;code&gt;git status&lt;/code&gt; and create a commit with the file name:&lt;/p&gt;

&lt;p&gt;Let's see how:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status &lt;span class="nt"&gt;--porcelain&lt;/span&gt; &lt;span class="c"&gt;# prints git status in a format that can be parsed&lt;/span&gt;
&lt;span class="c"&gt;# M utils/index.ts&lt;/span&gt;
&lt;span class="c"&gt;# M components/Button.tsx&lt;/span&gt;
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we pipe this result to &lt;code&gt;awk&lt;/code&gt; and print only the file name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status &lt;span class="nt"&gt;--porcelain&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;
&lt;span class="c"&gt;# utils/index.ts&lt;/span&gt;
&lt;span class="c"&gt;# components/Button.tsx&lt;/span&gt;
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;read each line (filename), and do a commit with &lt;code&gt;--no-verify&lt;/code&gt; to not trigger pre-commit check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;git status &lt;span class="nt"&gt;--porcelain&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; | 
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; line
&lt;span class="k"&gt;do
    &lt;/span&gt;git add &lt;span class="nv"&gt;$line&lt;/span&gt;
    git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add file &lt;/span&gt;&lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--no-verify&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running this script I had about 2000 commits where I could run &lt;a href="https://git-scm.com/docs/git-bisect"&gt;&lt;code&gt;git bisect&lt;/code&gt;&lt;/a&gt; and find the issue.&lt;br&gt;
&lt;code&gt;git bisect&lt;/code&gt; reduced the number of checks from &lt;strong&gt;2000&lt;/strong&gt; (theoretically) to just &lt;strong&gt;11&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I found the file that caused the issue, and it was completely unrelated to the file where the error was thrown. &lt;br&gt;
So I just eslint ignored the sorting rule in that file (till later investigation). &lt;br&gt;
Then I did a soft reset to undo all 2000 commits and pushed a single commit that successfully adds the new eslint plugin and rules to the project.&lt;/p&gt;

</description>
      <category>development</category>
      <category>bugs</category>
    </item>
    <item>
      <title>NodeJS: How to test the npm package before releasing</title>
      <dc:creator>Dragoș Străinu</dc:creator>
      <pubDate>Fri, 15 Apr 2022 00:00:00 +0000</pubDate>
      <link>https://forem.com/strdr4605/nodejs-how-to-test-the-npm-package-before-releasing-43io</link>
      <guid>https://forem.com/strdr4605/nodejs-how-to-test-the-npm-package-before-releasing-43io</guid>
      <description>&lt;p&gt;Let's say you have a &lt;strong&gt;core&lt;/strong&gt; package that is used in your &lt;strong&gt;app&lt;/strong&gt; and &lt;strong&gt;mobile&lt;/strong&gt; projects.&lt;/p&gt;

&lt;p&gt;Now you need to do some changes, both in &lt;strong&gt;core&lt;/strong&gt; and &lt;strong&gt;app&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the process of local development, you should test that &lt;strong&gt;core&lt;/strong&gt; is working correctly with &lt;strong&gt;app&lt;/strong&gt; before the release.&lt;br&gt;
You can use &lt;a href="https://docs.npmjs.com/cli/v8/commands/npm-link"&gt;&lt;code&gt;npm link&lt;/code&gt;&lt;/a&gt;, but from my experience, it's not always working as expected.&lt;/p&gt;

&lt;p&gt;A better alternative is &lt;a href="https://github.com/wclr/yalc"&gt;yalc&lt;/a&gt;. You can check it yourself, but shortly, as the package itself says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Better workflow than &lt;strong&gt;npm | yarn link&lt;/strong&gt; for package authors&lt;br&gt;
After everything is working locally, you are ready to do a Pull Request. &lt;br&gt;
But wait! The CI will fail because the new version of &lt;strong&gt;core&lt;/strong&gt; is only available locally.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To fix this, we need a prerelease version of the &lt;strong&gt;core&lt;/strong&gt; that will be installed in CI, or then other engineers will try it on their machines.&lt;/p&gt;

&lt;p&gt;Let's, say the previous version of &lt;strong&gt;core&lt;/strong&gt; was &lt;code&gt;1.0.1&lt;/code&gt;, and you are working on future &lt;code&gt;1.0.2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://semver.org/spec/v2.0.0-rc.1.html"&gt;Semver.org docs&lt;/a&gt; suggests to use &lt;code&gt;1.0.2-rc.1&lt;/code&gt; (release candidate),&lt;br&gt;
but this approach does not work well when you are working on a project where many engineers can release to &lt;strong&gt;core&lt;/strong&gt; before you.&lt;/p&gt;

&lt;p&gt;From my experience, the best is to have a prerelease version that is associated with some issue/ticket/task number in the format:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;&amp;lt;current version&amp;gt;&lt;/code&gt;&lt;/strong&gt;-&lt;strong&gt;&lt;code&gt;&amp;lt;issue number&amp;gt;&lt;/code&gt;&lt;/strong&gt;.&lt;strong&gt;&lt;code&gt;&amp;lt;WIP version&amp;gt;&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Example: &lt;code&gt;1.0.1-4605.0&lt;/code&gt;, &lt;code&gt;1.0.1-4605.1&lt;/code&gt; ...&lt;/p&gt;

&lt;p&gt;The important part before publishing such a prerelease version is to set a tag. &lt;br&gt;
If you don't set a tag, the package will be published as &lt;code&gt;latest&lt;/code&gt;, &lt;br&gt;
and then someone will try to install the &lt;strong&gt;core&lt;/strong&gt; package, it will get the wrong "&lt;code&gt;latest&lt;/code&gt;" version which may still be WIP.&lt;br&gt;&lt;br&gt;
For this, you might use the &lt;code&gt;dev&lt;/code&gt; tag.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, the workflow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;You do changes in &lt;strong&gt;core&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Use the WIP version of &lt;strong&gt;core&lt;/strong&gt; in &lt;strong&gt;app&lt;/strong&gt; locally using &lt;code&gt;yalc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;When the task is ready:

&lt;ul&gt;
&lt;li&gt;Change the version of &lt;strong&gt;core&lt;/strong&gt;: &lt;code&gt;1.0.1-4605.0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Publish the WIP version: &lt;code&gt;npm publish --tag dev&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Update the &lt;strong&gt;app&lt;/strong&gt; with WIP version of &lt;strong&gt;core&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Do move WIP prereleases if needed: &lt;code&gt;1.0.1-4605.1&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Create PRs for both projects&lt;/li&gt;
&lt;li&gt;When &lt;strong&gt;core&lt;/strong&gt; PR is approved

&lt;ul&gt;
&lt;li&gt;Merge &lt;strong&gt;core&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Set new version &lt;code&gt;1.0.2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Publish &lt;strong&gt;core&lt;/strong&gt;: &lt;code&gt;npm publish&lt;/code&gt; (&lt;code&gt;latest&lt;/code&gt; tag is default)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Update &lt;strong&gt;app&lt;/strong&gt; PR with new version of &lt;strong&gt;core&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Merge &lt;strong&gt;app&lt;/strong&gt; changes into master&lt;/li&gt;
&lt;li&gt;Happy coding&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>development</category>
      <category>node</category>
      <category>npm</category>
    </item>
    <item>
      <title>Keep your JavaScript repository clean</title>
      <dc:creator>Dragoș Străinu</dc:creator>
      <pubDate>Sat, 09 Apr 2022 11:11:14 +0000</pubDate>
      <link>https://forem.com/strdr4605/keep-your-javascript-repository-clean-5gg0</link>
      <guid>https://forem.com/strdr4605/keep-your-javascript-repository-clean-5gg0</guid>
      <description>&lt;p&gt;As your JavaScript project grows, you start adding more tools and settings that will improve the development experience.&lt;/p&gt;

&lt;p&gt;Nowadays, tools like eslint, husky, and tests are essential for any project.&lt;/p&gt;

&lt;p&gt;The problem is that most of these tools require a config file at the root of your project/repo.&lt;br&gt;
After some time, the root directory is full of config files, and it's a bit intimidating when you open it in the git hub.&lt;/p&gt;

&lt;p&gt;One elegant solution I found in &lt;a href="https://github.com/remirror/remirror" rel="noopener noreferrer"&gt;remirror&lt;/a&gt; repo.&lt;br&gt;
There all config files that should stay in the root of the project are actually in the &lt;code&gt;./support/root/&lt;/code&gt; directory,&lt;br&gt;
and they are locally symlinked when setting up the project.&lt;/p&gt;

&lt;p&gt;Inspired by their approach I created &lt;a href="https://github.com/strdr4605/symlink-config" rel="noopener noreferrer"&gt;symlink-config&lt;/a&gt;, which helps to migrate your project to this approach.&lt;/p&gt;

&lt;p&gt;Here is how repo looks on Github before and after:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;thead&gt;
  &lt;tr&gt;
    &lt;th&gt;Default&lt;/th&gt;
    &lt;th&gt;With symlink-config&lt;/th&gt;
  &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstrdr4605.com%2Froot-config-files-demo.png" alt="root config files demo"&gt;&lt;/td&gt;
      &lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstrdr4605.com%2Froot-symlink-config-files-demo.png" alt="root symlink config files demo"&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This demo has only 5 configs in the root, but imagine adding more tools, like tsconfig.json, jest.config.js, and others.&lt;/p&gt;

&lt;p&gt;When exploring the project locally, &lt;br&gt;
all these files will be in the root but being just a symlink file they will differ from the rest of the files and you may skip them visually.&lt;/p&gt;

&lt;p&gt;In the end, I think this tool is useful for big open source projects with a lot of config files, as it cleans up the entry point of the project (the repo root) and makes it more appealing to users.&lt;/p&gt;

&lt;p&gt;Check out &lt;a href="https://github.com/strdr4605/symlink-config" rel="noopener noreferrer"&gt;&lt;strong&gt;symlink-config&lt;/strong&gt;&lt;/a&gt;, and let me know your opinion!&lt;/p&gt;

</description>
      <category>tools</category>
      <category>node</category>
      <category>npm</category>
    </item>
    <item>
      <title>Optional pre-commit checks with husky</title>
      <dc:creator>Dragoș Străinu</dc:creator>
      <pubDate>Fri, 01 Apr 2022 09:48:14 +0000</pubDate>
      <link>https://forem.com/strdr4605/optional-pre-commit-checks-with-husky-5ei6</link>
      <guid>https://forem.com/strdr4605/optional-pre-commit-checks-with-husky-5ei6</guid>
      <description>&lt;p&gt;If you are working on a JavaScript project, you probably are using husky to check your &lt;a href="https://dev.to/commitlint-custom-commit-message-with-emojis"&gt;commit messages&lt;/a&gt;, &lt;br&gt;
maybe do some Prettier, Eslint formatting, or TypeScript checks.&lt;/p&gt;

&lt;p&gt;Checking commit messages is fast, but running formatting and type checking takes more time as your project grows.&lt;/p&gt;

&lt;p&gt;You can use &lt;a href="https://github.com/okonet/lint-staged"&gt;lint-staged&lt;/a&gt; for prettier and eslint, &lt;br&gt;
but using it for TypeScript check makes no sense because if you change types in a git staged file it may break typing in another file.&lt;/p&gt;

&lt;p&gt;All engineers have different workflows. To fix an issue from the TypeScript compiler, you could run a &lt;code&gt;tsc --watch&lt;/code&gt; process and make sure nothing is broken.&lt;/p&gt;

&lt;p&gt;I don't want to run &lt;code&gt;tsc --watch&lt;/code&gt; because constantly running it slows the laptop. I don't mind if this check will be done at pre-commit stage even if the commit will take 10,20,30+ seconds.&lt;/p&gt;

&lt;p&gt;But how to make this pre-commit optional? So the teammates that do manually formatting and type checking will not be frustrated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/husky"&gt;husky&lt;/a&gt; version 7 enables pre-commit configuration using a bash script. So why not do checking before running &lt;code&gt;tsc&lt;/code&gt; or &lt;code&gt;lint-staged&lt;/code&gt;?!&lt;/p&gt;

&lt;p&gt;This will be the beginning of the &lt;code&gt;.husky/pre-commit&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/_/husky.sh"&lt;/span&gt;

&lt;span class="nv"&gt;APP_PRE_COMMIT_OPTIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/_/pre-commit.options"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we can put our options in a file named &lt;code&gt;pre-commit.options&lt;/code&gt; inside the &lt;code&gt;.husky/_/&lt;/code&gt; folder.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;.husky/_/&lt;/code&gt; should have a &lt;code&gt;.gitingore&lt;/code&gt; file that will ignore our option file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, let's print some message if the options files do not exist&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="nv"&gt;YELLOW&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s2"&gt;33[1;33m"&lt;/span&gt;
&lt;span class="nv"&gt;GREEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s2"&gt;33[1;32m"&lt;/span&gt;
&lt;span class="nv"&gt;RESET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s2"&gt;33[0m"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$APP_PRE_COMMIT_OPTIONS&lt;/span&gt;&lt;span class="s2"&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;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;YELLOW&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Skipping pre-commit hook."&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"If you want to use pre-commit for TypeScript check and lint-staged, run:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GREEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;echo -e 'APP_TS=true;&lt;/span&gt;&lt;span class="se"&gt;\\\n&lt;/span&gt;&lt;span class="s2"&gt;APP_LINT=true;' &amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;$P_APP_PRE_COMMIT_OPTIONS&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RESET&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;YELLOW&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;It will add some delay before committing!&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RESET&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"
  exit 0
fi

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uWyp9P7c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://strdr4605.com/optional-pre-commit.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uWyp9P7c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://strdr4605.com/optional-pre-commit.png" alt="optional-pre-commit" width="880" height="163"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let's source the options file and check if the user enabled the linting:&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;source&lt;/span&gt; &lt;span class="nv"&gt;$APP_PRE_COMMIT_OPTIONS&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;APP_LINT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;APP_LINT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"true"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GREEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;[husky] [pre-commit] [lint-staged]&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RESET&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  npx lint-staged
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can add more options and checks if needed!&lt;/p&gt;

&lt;h2&gt;
  
  
  Final result
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;.husky/pre-commit&lt;/code&gt; file should look similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/_/husky.sh"&lt;/span&gt;

&lt;span class="nv"&gt;APP_PRE_COMMIT_OPTIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/_/pre-commit.options"&lt;/span&gt;

&lt;span class="nv"&gt;YELLOW&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s2"&gt;33[1;33m"&lt;/span&gt;
&lt;span class="nv"&gt;GREEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s2"&gt;33[1;32m"&lt;/span&gt;
&lt;span class="nv"&gt;RESET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s2"&gt;33[0m"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$APP_PRE_COMMIT_OPTIONS&lt;/span&gt;&lt;span class="s2"&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;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;YELLOW&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Skipping pre-commit hook."&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"If you want to use pre-commit for TypeScript check and lint-staged, run:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GREEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;echo -e 'APP_TS=true;&lt;/span&gt;&lt;span class="se"&gt;\\\n&lt;/span&gt;&lt;span class="s2"&gt;APP_LINT=true;' &amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;$P_APP_PRE_COMMIT_OPTIONS&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RESET&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;YELLOW&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;It will add some delay before committing!&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RESET&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"
  exit 0
fi

source &lt;/span&gt;&lt;span class="nv"&gt;$APP_PRE_COMMIT_OPTIONS&lt;/span&gt;&lt;span class="s2"&gt;

if [ -n "&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;APP_TS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" ] &amp;amp;&amp;amp; [ "&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;APP_TS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" == "&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="s2"&gt;" ]; then
  echo "&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GREEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;husky] &lt;span class="o"&gt;[&lt;/span&gt;pre-commit] &lt;span class="o"&gt;[&lt;/span&gt;tsc]&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RESET&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"
  npx tsc
fi

if [ -n "&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;APP_LINT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" ] &amp;amp;&amp;amp; [ "&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;APP_LINT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" == "&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="s2"&gt;" ]; then
  echo "&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GREEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;husky] &lt;span class="o"&gt;[&lt;/span&gt;pre-commit] &lt;span class="o"&gt;[&lt;/span&gt;lint-staged]&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RESET&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"
  npx lint-staged
fi
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>git</category>
      <category>node</category>
      <category>tools</category>
    </item>
    <item>
      <title>NPM: How to document your package.json scripts</title>
      <dc:creator>Dragoș Străinu</dc:creator>
      <pubDate>Sun, 27 Mar 2022 17:13:28 +0000</pubDate>
      <link>https://forem.com/strdr4605/npm-how-to-document-your-packagejson-scripts-3lek</link>
      <guid>https://forem.com/strdr4605/npm-how-to-document-your-packagejson-scripts-3lek</guid>
      <description>&lt;p&gt;As your project grows you add more scripts to package.json.&lt;br&gt;
When a new member joins the project (or maybe you come back after a break) it's hard to understand from the script itself, &lt;br&gt;
what it is doing and why it was created, especially when the script is 80 chars long with a lot of params and &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's see what we do about this:&lt;/p&gt;
&lt;h2&gt;
  
  
  README.md
&lt;/h2&gt;

&lt;p&gt;You could add documentation for the scripts in a section of the README.md file.&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="gu"&gt;## NPM scripts&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="gs"&gt;**start**&lt;/span&gt;: Description for &lt;span class="sb"&gt;`npm start`&lt;/span&gt; script
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**test**&lt;/span&gt;: Description for &lt;span class="sb"&gt;`npm test`&lt;/span&gt; script
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👍This approach is nice because you can use the markdown rich text features. Also, the README file is the first file opened by a new member of the team.&lt;/p&gt;

&lt;p&gt;🙁A downside of this approach is that when you see a script in package.json you need to open the README, then do back to package.json or terminal (to run the command).&lt;/p&gt;

&lt;h2&gt;
  
  
  package.json
&lt;/h2&gt;

&lt;p&gt;Npm does not support &lt;a href="https://json5.org/"&gt;JSON5&lt;/a&gt; for package.json that could allow comments in JSON for example in tsconfig.json.&lt;/p&gt;

&lt;p&gt;There is a popular question on StackOverflow on the topic:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/14221579/how-do-i-add-comments-to-package-json-for-npm-install"&gt;How do I add comments to package.json for npm install?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can read all proposed variants (and discussions) on StackOverflow. One option that could be ok is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&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;"package name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.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;"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;"package description"&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="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm install &amp;amp;&amp;amp; node server.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="nl"&gt;"scriptsComments"&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;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Runs development build on a local server configured by server.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="err"&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;👍This approach is nice because script documentation stays closer to the script itself.&lt;/p&gt;

&lt;p&gt;🙁The downside: as your package.json grows with more scripts, dependencies, other tools configs it's very easy to get lost.&lt;/p&gt;

&lt;h2&gt;
  
  
  npx why
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;npx why&lt;/code&gt; is a tool created to fix the problem of documenting package.json scripts.&lt;/p&gt;

&lt;p&gt;If we have to run the scripts from the terminal, why not have the documentation for the scripts in the terminal?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bfyzwOFo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://raw.githubusercontent.com/strdr4605/why/master/support/assets/demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bfyzwOFo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://raw.githubusercontent.com/strdr4605/why/master/support/assets/demo.gif" alt="npx why demo" width="600" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx why --init&lt;/code&gt; will create a &lt;strong&gt;package-why.json&lt;/strong&gt; in the root of the project with all scripts from package.json and the default descriptions.&lt;/p&gt;

&lt;p&gt;After this, calling &lt;code&gt;npx why&lt;/code&gt; will print all scripts and descriptions.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx why &amp;lt;some script name&amp;gt;&lt;/code&gt; will print only description for &lt;code&gt;npm run &amp;lt;some script name&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You could update the documention by passing a description to &lt;code&gt;npx why &amp;lt;some script name&amp;gt; "&amp;lt;script description&amp;gt;"&lt;/code&gt;. Or changing &lt;strong&gt;package-why.json&lt;/strong&gt; file directly.&lt;/p&gt;

&lt;p&gt;For better experience I suggest installing the package as a development dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;why &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🎉This solution for documenting the script is nice because you access the docs from the terminal (where you have to use the script). &lt;br&gt;
Also I am sure that with new updates and features the experience of using &lt;code&gt;why&lt;/code&gt; will get better and better.&lt;/p&gt;

&lt;p&gt;🙁The downside: the process of documenting script is not so comfortable because you have to move between package-why.json and package.json files.&lt;/p&gt;

&lt;p&gt;Check out &lt;a href="https://github.com/strdr4605/why#readme"&gt;&lt;code&gt;npx why&lt;/code&gt; package&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>npm</category>
      <category>node</category>
      <category>tools</category>
    </item>
    <item>
      <title>You need to use Git worktree</title>
      <dc:creator>Dragoș Străinu</dc:creator>
      <pubDate>Wed, 16 Mar 2022 14:03:44 +0000</pubDate>
      <link>https://forem.com/strdr4605/you-need-to-use-git-worktree-2f2f</link>
      <guid>https://forem.com/strdr4605/you-need-to-use-git-worktree-2f2f</guid>
      <description>&lt;p&gt;Recently I found &lt;code&gt;git worktree&lt;/code&gt; feature and I plan to use it in all my projects.&lt;br&gt;
Here is why and how you can use it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why
&lt;/h2&gt;

&lt;p&gt;If you are working on a big project with a lot of issues and tasks, you constantly have to switch between branches to do some quick-fix changes or maybe review a PR from a teammate.&lt;br&gt;
The problem is that often you have some work in progress and switching branches means that you need to commit or stash your WIP changes.&lt;br&gt;
If it's only 1-2 files/changes the cost of commit/stash is not that high. But if you work on a big feature with over 9000 changes it's hard to constantly change branches.&lt;/p&gt;
&lt;h2&gt;
  
  
  How
&lt;/h2&gt;

&lt;p&gt;Basically &lt;code&gt;git worktree&lt;/code&gt; command allows to create a new directory with desired git state.&lt;br&gt;&lt;br&gt;
Actually, your current workspace is already in git worktree.&lt;br&gt;&lt;br&gt;
You can type &lt;code&gt;git worktree list&lt;/code&gt; and you will see one item, which is your current git workspace.&lt;/p&gt;

&lt;p&gt;Let's say you work on a big feature in your work project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree list
~/Work/project  17d8aab &lt;span class="o"&gt;[&lt;/span&gt;big-feature]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a quick-fix needs to be done from the &lt;code&gt;origin/master&lt;/code&gt;.&lt;br&gt;
You can create a git worktree with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree add &lt;span class="nt"&gt;-b&lt;/span&gt; quick-fix ../quickfix origin/master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will create a new directory named &lt;strong&gt;quickfix&lt;/strong&gt; at the same level as your &lt;strong&gt;project&lt;/strong&gt; directory.&lt;br&gt;&lt;br&gt;
The branch will be &lt;code&gt;quick-fix&lt;/code&gt; and it will be set at commit from &lt;code&gt;origin/master&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
You can see your git worktree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree list
~/Work/project   17d8aab &lt;span class="o"&gt;[&lt;/span&gt;big-feature]
~/Work/quickfix  25e6c4b &lt;span class="o"&gt;[&lt;/span&gt;quick-fix]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, you can navigate to &lt;strong&gt;quickfix&lt;/strong&gt; directory, do your changes, push to remote, merge.&lt;/p&gt;

&lt;p&gt;After you are done with &lt;strong&gt;quickfix&lt;/strong&gt; you can remove the workspace with &lt;code&gt;git worktree remove quickfix&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;This approach of using git worktree is not the most elegant but it's doing the job.&lt;br&gt;
I might change the workflow (using bare git repos instead of normal repos) and will update this post.&lt;br&gt;
For now you may check &lt;a href="https://morgan.cugerone.com/blog/how-to-use-git-worktree-and-in-a-clean-way/"&gt;How to use git worktree and in a clean way&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But I suggest to play a bit with git worktree if you have problems from &lt;strong&gt;Why&lt;/strong&gt; section. And adding this git feature to your toolkit will make you a more productive engineer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Let me know if you have a nice workflow for git worktree!&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>git</category>
    </item>
    <item>
      <title>Use Caps Lock to trigger iTerm2 terminal</title>
      <dc:creator>Dragoș Străinu</dc:creator>
      <pubDate>Tue, 22 Feb 2022 09:33:50 +0000</pubDate>
      <link>https://forem.com/strdr4605/use-caps-lock-to-trigger-iterm2-terminal-3nmj</link>
      <guid>https://forem.com/strdr4605/use-caps-lock-to-trigger-iterm2-terminal-3nmj</guid>
      <description>&lt;p&gt;A long time ago someone showed me that it's possible to add a hotkey to iTerm2 to open the terminal window from anywhere in the system. Till these days I was using &lt;code&gt;`&lt;/code&gt; key as a hotkey. But when having to input &lt;code&gt;`&lt;/code&gt; in markdown or JS I had to use &lt;code&gt;Option + `&lt;/code&gt; which has specific behavior.&lt;/p&gt;

&lt;p&gt;I decided to try Caps Lock for a while (as Caps Lock is one of the useless keys on the keyboard).&lt;/p&gt;

&lt;p&gt;The issue is that iTerm2 does not recognize the Caps Lock press. So I have to remap in MacOS Caps Lock to Control key.&lt;/p&gt;

&lt;p&gt;Go to &lt;strong&gt;System Preferences &amp;gt; Keyboard&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select Modifier Keys:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gid8bZ68--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2qd6db80pqn0zwmz8dqa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gid8bZ68--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2qd6db80pqn0zwmz8dqa.png" alt="macos keyboard mod keys" width="880" height="799"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set Caps Lock as Control key: &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bRHuYCp6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zlhyph4j66j4mrgeg169.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bRHuYCp6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zlhyph4j66j4mrgeg169.png" alt="macos caps lock ctrl" width="880" height="799"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to iTerm, &lt;strong&gt;System Preferences &amp;gt; Profiles &amp;gt; Select profile &amp;gt; Keys &amp;gt; Configure Hotkey Window&lt;/strong&gt;:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BWcq4yGI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nwcf1gqy1r4pvjm2o43b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BWcq4yGI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nwcf1gqy1r4pvjm2o43b.png" alt="iterm add hotkey" width="880" height="425"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set Double-tap key as Control&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cXWKYdWT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g5oi1njvscj14zbyfmsy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cXWKYdWT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g5oi1njvscj14zbyfmsy.png" alt="iterm ctrl hotkey" width="578" height="208"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now when you double press Caps Lock (or Ctrl) the iTerm window will popup.&lt;/p&gt;

</description>
      <category>tools</category>
      <category>iterm2</category>
      <category>terminal</category>
    </item>
    <item>
      <title>Nudge Github PR reviewers with Slack API</title>
      <dc:creator>Dragoș Străinu</dc:creator>
      <pubDate>Thu, 17 Feb 2022 20:21:44 +0000</pubDate>
      <link>https://forem.com/strdr4605/nudge-github-pr-reviewers-with-slack-api-djb</link>
      <guid>https://forem.com/strdr4605/nudge-github-pr-reviewers-with-slack-api-djb</guid>
      <description>&lt;p&gt;You know that moment when you have a Pull Request waiting for someone to review but days pass and no one is looking at it, so you have to manually ask reviewers in a public channel or privately. How we can automate this?&lt;/p&gt;

&lt;p&gt;Previously I was using Bitbucket and &lt;a href="https://slack.com/apps/A8W8QLZD1-bitbucket-cloud?tab=more_info" rel="noopener noreferrer"&gt;Bitbucket Cloud&lt;/a&gt; have a cool feature to nudge reviewers from Slack and it will send a private message notifying reviewers to take a look at the Pull Request.&lt;/p&gt;

&lt;p&gt;Now I am using Github with &lt;a href="https://slack.com/apps/A01BP7R4KNY-github?tab=more_info" rel="noopener noreferrer"&gt;Github for Slack&lt;/a&gt; and it has a feature to remind reviewers at a specific time of the day about PRs. But I could not find a nudge reviewer feature. I searched on the Slack app store but still had no success.&lt;/p&gt;

&lt;p&gt;I decided to do an MVP for my project and team, not publicly available, but you can create the same feature for your project/team/company.&lt;/p&gt;

&lt;h2&gt;
  
  
  Slack App
&lt;/h2&gt;

&lt;p&gt;To publish messages to Slack workspace you need to create a &lt;a href="https://api.slack.com/apps" rel="noopener noreferrer"&gt;Slack App&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Create New App&lt;/strong&gt; button&lt;/li&gt;
&lt;li&gt;Pick &lt;strong&gt;From scratch&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;App Name&lt;/strong&gt; to &lt;code&gt;&amp;lt;Company name&amp;gt; PR nudge&lt;/code&gt; (or any other suitable name)&lt;/li&gt;
&lt;li&gt;Pick your company workspace&lt;/li&gt;
&lt;li&gt;Press &lt;strong&gt;Create App&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Next, we need to create a webhook for a Slack channel.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In &lt;strong&gt;Add features and functionality&lt;/strong&gt; section select &lt;strong&gt;Incoming Webhooks&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add New Webhook to Workspace&lt;/strong&gt; and select a channel where all messages from &lt;a href="https://slack.com/apps/A01BP7R4KNY-github?tab=more_info" rel="noopener noreferrer"&gt;Github for Slack&lt;/a&gt; goes (Usually this channel is named &lt;code&gt;#dev&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now we have a webhook that will post messages to our channel, next we need to call this webhook from somewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Userscript
&lt;/h2&gt;

&lt;p&gt;Let's build a userscript that will add a &lt;strong&gt;Nudge reviewers&lt;/strong&gt; button into Github PR page, that when clicked will nudge in Slack, reviewers that are awaited for review.&lt;/p&gt;

&lt;p&gt;If you don't know about userscripts you can check my explanation video: &lt;a href="https://youtu.be/9yFi3-cfprQ" rel="noopener noreferrer"&gt;You Don't Know *.user.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, your need a Userscript manager extension, let's install &lt;a href="https://www.tampermonkey.net/" rel="noopener noreferrer"&gt;Tampermonkey&lt;/a&gt;.&lt;br&gt;
After installation open Tampermonkey and create a new script.&lt;/p&gt;
&lt;h3&gt;
  
  
  Userscript Header
&lt;/h3&gt;

&lt;p&gt;We start with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ==UserScript==&lt;/span&gt;
&lt;span class="c1"&gt;// @name         &amp;lt;Your Company&amp;gt; PR nudge&lt;/span&gt;
&lt;span class="c1"&gt;// @namespace    https://github.com/&amp;lt;Your Company&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// @version      1.0.0&lt;/span&gt;
&lt;span class="c1"&gt;// @description  Nudge PR reviewers&lt;/span&gt;
&lt;span class="c1"&gt;// @author       You&lt;/span&gt;
&lt;span class="c1"&gt;// @match        https://github.com/&amp;lt;Your Company&amp;gt;/*/pull/*&lt;/span&gt;
&lt;span class="c1"&gt;// @icon         https://www.google.com/s2/favicons?sz=64&amp;amp;domain=github.com&lt;/span&gt;
&lt;span class="c1"&gt;// @grant        GM_xmlhttpRequest&lt;/span&gt;
&lt;span class="c1"&gt;// @connect      slack.com&lt;/span&gt;
&lt;span class="c1"&gt;// ==/UserScript==&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="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// Your code here...&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@match https://github.com/&amp;lt;Your Company&amp;gt;/*/pull/*&lt;/code&gt; to run the script only when navigating to a PR page, such as
&lt;code&gt;https://github.com/Org/Repo/pull/4605&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@grant GM_xmlhttpRequest&lt;/code&gt; to do requests to Slack webhook&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@connect slack.com&lt;/code&gt; to allow requests to Slack&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Nudge reviewers button
&lt;/h3&gt;

&lt;p&gt;Now let's create a &lt;strong&gt;Nudge reviewers&lt;/strong&gt; button and add it to the Reviewers section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="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="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use strict&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;PR_NUDGE_WEBHOOK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://hooks.slack.com/services/********/***********/************************&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;nudgeButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;nudgeButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Nudge reviewers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;nudgeButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;btn btn-sm js-details-target d-inline-block float-left float-none m-0 mr-md-0 js-title-edit-button&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;reviewersFormElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reviewers-select-menu&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;parentElement&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstrdr4605.com%2Fnudge_reviewers_button.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%2Fstrdr4605.com%2Fnudge_reviewers_button.png" alt="nudge_reviewers_button"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Who needs to review
&lt;/h3&gt;

&lt;p&gt;This function will return a list with Github nicknames that should review this PR.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAllAwaiting&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;allReviewers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reviewersFormElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;p&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;awaiting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;allReviewers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;reviewerEl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;reviewerEl&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;span[aria-label]&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="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aria-label&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="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Awaiting&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;awaitingNicknames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;awaiting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reviewerEl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;reviewerEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;innerText&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;awaitingNicknames&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Mapping Github user with Slack user
&lt;/h3&gt;

&lt;p&gt;Here is a sad part of this script, because we need to map Github with Slack, to know whom to nudge on Slack.&lt;br&gt;
At this point, this is a manual process and this is the only downside of this solution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="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="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use strict&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;GithubSlackMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;strdr4605&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;U**********&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PR_NUDGE_WEBHOOK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To find the member ID from Slack, go to a Slack user profile, click &lt;strong&gt;More&lt;/strong&gt;, and click &lt;strong&gt;Copy member ID&lt;/strong&gt;.&lt;br&gt;
If you have 30+ engineers in your team it may take a while. For me, it was 11 engineers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you have an idea how to automate this, let me know.&lt;/p&gt;
&lt;h3&gt;
  
  
  onNudgeBtnClick
&lt;/h3&gt;

&lt;p&gt;Now let's gather all the data and sent a request to Slack webhook.&lt;br&gt;
Slack has a specific format style for messages, so to tag people we need &lt;code&gt;slackMention&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;slackMention&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;githugNickname&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;GithubSlackMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;githugNickname&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;@&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;GithubSlackMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;githugNickname&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;`&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;githugNickname&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onNudgeBtnClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&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;prLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&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;prTitle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; by &lt;/span&gt;&lt;span class="dl"&gt;"&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="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;prLink&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;awaitingReviewers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getAllAwaiting&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;awaitingReviewers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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="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;dataObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Hey &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;awaitingReviewers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;slackMention&lt;/span&gt;
    &lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;,\nI really need your review on &amp;lt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;prLink&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;prTitle&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;. 🥺😇🙏😊`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nc"&gt;GM_xmlhttpRequest&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&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="nx"&gt;PR_NUDGE_WEBHOOK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataObj&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;responseType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;nocache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;onload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;onerror&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add button to the page
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;nudgeButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onNudgeBtnClick&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;reviewersFormElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nudgeButton&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final result
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ==UserScript==&lt;/span&gt;
&lt;span class="c1"&gt;// @name         &amp;lt;Your Company&amp;gt; PR nudge&lt;/span&gt;
&lt;span class="c1"&gt;// @namespace    https://github.com/&amp;lt;Your Company&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// @version      1.0.0&lt;/span&gt;
&lt;span class="c1"&gt;// @description  Nudge PR reviewers&lt;/span&gt;
&lt;span class="c1"&gt;// @author       You&lt;/span&gt;
&lt;span class="c1"&gt;// @match        https://github.com/&amp;lt;Your Company&amp;gt;/*/pull/*&lt;/span&gt;
&lt;span class="c1"&gt;// @icon         https://www.google.com/s2/favicons?sz=64&amp;amp;domain=github.com&lt;/span&gt;
&lt;span class="c1"&gt;// @grant        GM_xmlhttpRequest&lt;/span&gt;
&lt;span class="c1"&gt;// @connect      slack.com&lt;/span&gt;
&lt;span class="c1"&gt;// ==/UserScript==&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="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use strict&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;GithubSlackMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;strdr4605&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;U**********&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PR_NUDGE_WEBHOOK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://hooks.slack.com/services/********/***********/************************&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;nudgeButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;nudgeButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Nudge reviewers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;nudgeButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;btn btn-sm js-details-target d-inline-block float-left float-none m-0 mr-md-0 js-title-edit-button&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;reviewersFormElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reviewers-select-menu&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;parentElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAllAwaiting&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;allReviewers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reviewersFormElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;p&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;awaiting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;allReviewers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;reviewerEl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;reviewerEl&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;span[aria-label]&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="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aria-label&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="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Awaiting&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;awaitingNicknames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;awaiting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reviewerEl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;reviewerEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;innerText&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;awaitingNicknames&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;slackMention&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;githugNickname&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;GithubSlackMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;githugNickname&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;@&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;GithubSlackMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;githugNickname&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;`&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;githugNickname&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onNudgeBtnClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&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;prLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&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;prTitle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; by &lt;/span&gt;&lt;span class="dl"&gt;"&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="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;prLink&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;awaitingReviewers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getAllAwaiting&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;awaitingReviewers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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="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;dataObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Hey &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;awaitingReviewers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;slackMention&lt;/span&gt;
      &lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;,\nI really need your review on &amp;lt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;prLink&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;prTitle&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;. 🥺😇🙏😊`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nc"&gt;GM_xmlhttpRequest&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&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="nx"&gt;PR_NUDGE_WEBHOOK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataObj&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;responseType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;nocache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;onload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="na"&gt;onerror&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;err&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="nx"&gt;nudgeButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onNudgeBtnClick&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;reviewersFormElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nudgeButton&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1000&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;Now when clicking &lt;strong&gt;Nudge reviewers&lt;/strong&gt; your teammates will be notified and will review your changes faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final step
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't expose webhook URL to the public!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Add the script to a private repo(&lt;code&gt;company-userscripts&lt;/code&gt;) in your Github org.&lt;br&gt;
Name it &lt;code&gt;PR-nudge.user.js&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
Tell teammates to install Tampermonkey and send them:&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/Org/org-userscripts/blob/master/PR-nudge.user.js?raw=1" rel="noopener noreferrer"&gt;https://github.com/Org/org-userscripts/blob/master/PR-nudge.user.js?raw=1&lt;/a&gt;&lt;br&gt;&lt;br&gt;
to add the script on their machine.&lt;/p&gt;

</description>
      <category>tools</category>
      <category>github</category>
      <category>slack</category>
      <category>userscript</category>
    </item>
    <item>
      <title>Git: hide specific branch when doing git log --all</title>
      <dc:creator>Dragoș Străinu</dc:creator>
      <pubDate>Thu, 17 Feb 2022 20:21:23 +0000</pubDate>
      <link>https://forem.com/strdr4605/git-hide-specific-branch-when-doing-git-log-all-226i</link>
      <guid>https://forem.com/strdr4605/git-hide-specific-branch-when-doing-git-log-all-226i</guid>
      <description>&lt;p&gt;I deploy &lt;a href="https://strdr4605.com"&gt;https://strdr4605.com&lt;/a&gt; on Github pages. Deployment and hosting are on &lt;code&gt;gh-pages&lt;/code&gt; branch.&lt;br&gt;
When doing my usual check of branch status and history using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git log &lt;span class="nt"&gt;--oneline&lt;/span&gt; &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;--graph&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I faced this problem:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DZ-Ydppm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0vyge1k0xrromzksoodb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DZ-Ydppm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0vyge1k0xrromzksoodb.png" alt="git gh-pages branch" width="880" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I see a very long list of commits on &lt;code&gt;gh-pages&lt;/code&gt; branch and I need to scroll down to see my other branches.&lt;/p&gt;

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

&lt;p&gt;You can hide a specific branch using &lt;code&gt;--exclude&lt;/code&gt; option for &lt;code&gt;git log&lt;/code&gt;. Now my command looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
git log &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--oneline&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--graph&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--exclude&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;refs/remotes/origin/gh-pages &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it looks better:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xAqHWEO5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kuf2wf4h6rhaple116vz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xAqHWEO5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kuf2wf4h6rhaple116vz.png" alt="git hide gh-pages branch" width="880" height="117"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can add this command in a git alias.&lt;/p&gt;

&lt;p&gt;If needed check &lt;a href="https://stackoverflow.com/questions/9437182/git-show-all-branches-but-not-stashes-in-log"&gt;here&lt;/a&gt; for more things to exclude from &lt;code&gt;git log&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can check why I prefer to do long commands instead of git aliases at &lt;a href="https://strdr4605.com/how-i-use-git"&gt;How I use Git&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>git</category>
    </item>
  </channel>
</rss>
