<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[VS Writings]]></title><description><![CDATA[Crazy Developer From India, who has more interest in programming than a main stream academic!

I ❤️ Open Source!]]></description><link>https://blog.svarun.dev</link><generator>RSS for Node</generator><lastBuildDate>Fri, 10 Apr 2026 15:11:08 GMT</lastBuildDate><atom:link href="https://blog.svarun.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[🔓 How I Rooted My OnePlus Nord CE 4 (Android 14) and Set It Up for Livestream Automation]]></title><description><![CDATA[After successfully downgrading from Android 15 to a stable Android 14 build, my next goal was to root my OnePlus Nord CE 4. The reason? I needed complete control over USB permissions, background service stability, and the ability to automate things l...]]></description><link>https://blog.svarun.dev/how-i-rooted-my-oneplus-nord-ce-4-android-14-and-set-it-up-for-livestream-automation</link><guid isPermaLink="true">https://blog.svarun.dev/how-i-rooted-my-oneplus-nord-ce-4-android-14-and-set-it-up-for-livestream-automation</guid><dc:creator><![CDATA[Varun Sridharan]]></dc:creator><pubDate>Tue, 22 Apr 2025 11:15:39 GMT</pubDate><content:encoded><![CDATA[<p>After successfully downgrading from Android 15 to a stable Android 14 build, my next goal was to <strong>root my OnePlus Nord CE 4</strong>. The reason? I needed <strong>complete control</strong> over USB permissions, background service stability, and the ability to automate things like screen sharing and USB dialogs — all critical for my <strong>livestreaming workflow</strong>.</p>
<hr />
<h2 id="heading-my-setup">🧩 My Setup</h2>
<ul>
<li><p><strong>Device:</strong> OnePlus Nord CE 4 (CPH2613)</p>
</li>
<li><p><strong>Firmware:</strong> Android 14 – <code>10085_sign_CPH2613_11_A_OTA_0360_all_LC1KTR_00011011</code></p>
</li>
<li><p><strong>Goal:</strong> Root the phone for full control + automation with MacroDroid, CameraFI Live, RustDesk, etc.</p>
</li>
</ul>
<hr />
<h2 id="heading-unlocking-the-bootloader">🔓 Unlocking the Bootloader</h2>
<p>I started by unlocking the bootloader. Here's what I did:</p>
<ol>
<li><p>Enabled <strong>Developer Options</strong></p>
</li>
<li><p>Turned on <strong>USB Debugging</strong></p>
</li>
<li><p>Enabled <strong>OEM Unlock</strong></p>
</li>
<li><p>Downloaded <a target="_blank" href="https://developer.android.com/tools/releases/platform-tools">Android Platform Tools</a></p>
</li>
<li><p>Installed Google USB Drivers</p>
</li>
<li><p>Ran: <code>adb reboot bootloader</code><br /> <code>fastboot flashing unlock</code></p>
</li>
</ol>
<p>✔️ <strong>Yes</strong>, the process wiped my data as expected. Always back up before doing this.</p>
<hr />
<h2 id="heading-the-rooting-roadblock">🧙 The Rooting Roadblock</h2>
<p>My first approach was traditional:</p>
<ul>
<li><p>I extracted <code>boot.img</code> from the full OTA (<code>10085_sign_CPH2613_11_A_OTA_0360_all_LC1KTR_00011011</code>)</p>
</li>
<li><p>Tried patching it using Magisk and flashing via fastboot</p>
</li>
</ul>
<p>But no luck — it <strong>didn't boot properly</strong>, and attempts led to <strong>bootloops or stuck bootloader</strong>.</p>
<hr />
<h2 id="heading-the-better-way-twrp-magisk-zip">🔁 The Better Way: TWRP + Magisk Zip</h2>
<p>After hitting walls with <code>boot.img</code> patching, I found a <strong>better and more stable method</strong> thanks to this helpful YouTube video:🎥 <a target="_blank" href="https://www.youtube.com/watch?v=zwlO8-FPrhE">Watch here</a></p>
<h3 id="heading-heres-what-worked">Here’s what worked:</h3>
<ol>
<li><p>I downloaded a custom TWRP recovery for Nord CE 4: 🔗 <a target="_blank" href="https://sourceforge.net/projects/sheshu/files/benz/recovery/twrp_benz_v1_beta.img/download">TWRP by sheshu (SourceForge)</a></p>
</li>
<li><p>Flashed it via fastboot:`<code>fastboot boot twrp_benz_v1_beta.img</code>`</p>
</li>
<li><p>Booted into TWRP and installed <strong>Magisk 28.1</strong> (zip)<br /> 🔗 <a target="_blank" href="https://magisk.zip/">Magisk ZIP</a></p>
</li>
</ol>
<p>After reboot — 🔓 <strong>Root access was ready!</strong></p>
<h2 id="heading-troubleshooting-moment">🛠️ Troubleshooting Moment</h2>
<p>At one point, I accidentally flashed the wrong boot image and ended up <strong>stuck in a bootloader loop</strong>.<br />Luckily, reflashing the correct <code>boot.img</code> recovered the device. Always <strong>double-check file versions</strong> before flashing!</p>
<hr />
<h2 id="heading-root-setup-status">✅ Root Setup Status</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Feature</td><td>Status</td></tr>
</thead>
<tbody>
<tr>
<td></td></tr>
</tbody>
</table>
</div><table><tbody><tr><td><p>Magisk Version</p></td><td><p>28.1</p></td></tr></tbody></table>

<table><tbody><tr><td><p>Zygisk</p></td><td><p>❌ Not Enabled</p></td></tr></tbody></table>

<table><tbody><tr><td><p>Magisk Modules</p></td><td><p>❌ None Installed</p></td></tr></tbody></table>

<table><tbody><tr><td><p>MacroDroid</p></td><td><p>❌ Not Yet Setup</p></td></tr></tbody></table>

<p>But now that root is confirmed, I’m ready to start automating my entire livestreaming stack.</p>
<hr />
<h2 id="heading-final-thoughts">💡 Final Thoughts</h2>
<p>If you're using your OnePlus Nord CE 4 for something specialized — like <strong>live streaming, automation, or development</strong> — rooting it with this TWRP + Magisk ZIP method is much more stable than the patched <code>boot.img</code> route (which didn’t work in my case).</p>
<p>For me, rooting this device has opened the door to full control, background process reliability, and deeper automation. And since it's not my daily driver, there's <strong>no risk — only freedom.</strong></p>
]]></content:encoded></item><item><title><![CDATA[📉 How I Accidentally Upgraded to Android 15 and Downgraded Back to Android 14 on My OnePlus Nord CE 4]]></title><description><![CDATA[⚙️ Why I Use This Phone
I use my OnePlus Nord CE 4 exclusively for livestreaming via an external camera. It’s not my personal phone — it’s part of a dedicated setup that includes CameraFI Live, RustDesk, and MacroDroid. Because of this specific use, ...]]></description><link>https://blog.svarun.dev/how-i-accidentally-upgraded-to-android-15-and-downgraded-back-to-android-14-on-my-oneplus-nord-ce-4</link><guid isPermaLink="true">https://blog.svarun.dev/how-i-accidentally-upgraded-to-android-15-and-downgraded-back-to-android-14-on-my-oneplus-nord-ce-4</guid><dc:creator><![CDATA[Varun Sridharan]]></dc:creator><pubDate>Tue, 22 Apr 2025 11:03:32 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-why-i-use-this-phone">⚙️ Why I Use This Phone</h2>
<p>I use my OnePlus Nord CE 4 <strong>exclusively for livestreaming</strong> via an external camera. It’s not my personal phone — it’s part of a dedicated setup that includes CameraFI Live, RustDesk, and MacroDroid. Because of this specific use, stability and automation control are <strong>critical</strong> for me.</p>
<hr />
<h2 id="heading-how-i-ended-up-on-android-15">🤦 How I Ended Up on Android 15</h2>
<p>I received an OTA update notification, and without checking the version properly, I went ahead and clicked <strong>“Install”</strong>. I <em>kind of knew</em> Android 15 was rolling out, but I didn’t double-check.</p>
<p>Right after the update:</p>
<ul>
<li><p>The <strong>UI looked different</strong></p>
</li>
<li><p>Some <strong>permission dialogs changed</strong></p>
</li>
<li><p>And overall, the system felt <strong>less predictable</strong> for my livestream use case</p>
</li>
</ul>
<hr />
<h2 id="heading-why-i-wanted-to-go-back">🚨 Why I Wanted to Go Back</h2>
<p>Android 14 had been <strong>rock-solid</strong> for my livestreaming setup. With Android 15, I started worrying about:</p>
<ul>
<li><p>Background service interruptions</p>
</li>
<li><p>USB permission popups behaving differently</p>
</li>
<li><p>Compatibility with automation scripts in MacroDroid</p>
</li>
</ul>
<p>So I decided to <strong>downgrade</strong> back to Android 14.</p>
<hr />
<h2 id="heading-local-downgrade-didnt-work">❌ Local Downgrade Didn’t Work</h2>
<p>I first tried the usual local upgrade path:</p>
<ul>
<li><p>Downloading firmware via Oxygen Updater</p>
</li>
<li><p>Looking for older full OTA zips</p>
</li>
</ul>
<p>But OnePlus no longer publicly provides older full OTA firmware. Oxygen Updater also only showed the latest Android 15 builds.</p>
<hr />
<h2 id="heading-found-the-fix-on-xda">🧠 Found the Fix on XDA</h2>
<p>After spending hours searching forums, I stumbled across a <strong>lifesaver post on the OnePlus Community</strong>: 🔗 <a target="_blank" href="https://community.oneplus.com/thread/1733980920295194629">OnePlus OTA Archive Thread</a></p>
<p>It had <strong>full OTA zips by model and version</strong>, including the one I needed: `<code>10085_sign_CPH2613_11_A_OTA_0360_all_LC1KTR_</code><a target="_blank" href="http://00011011.zip"><code>00011011.zip</code></a>`<br />This was Android 14 build: <strong>14.0.1.420EX01</strong></p>
<hr />
<h2 id="heading-downgrade-process-local-upgrade-worked">🧰 Downgrade Process: Local Upgrade Worked!</h2>
<p>Even though many people online said EDL or fastboot was required — I was lucky. I just:</p>
<ol>
<li><p>Copied the OTA zip file to my phone’s internal storage</p>
</li>
<li><p>Renamed it appropriately if needed</p>
</li>
<li><p>Opened <strong>Settings &gt; System &gt; Local Update</strong></p>
</li>
<li><p>Chose the file and installed it</p>
</li>
</ol>
<p>No payload extraction, no fastboot flashing, no QFIL — it just worked. 🙏</p>
<hr />
<h2 id="heading-what-about-drivers-or-fastboot-problems">🛠️ What About Drivers or Fastboot Problems?</h2>
<p>Surprisingly — <strong>none.</strong></p>
<ul>
<li><p>No driver issues</p>
</li>
<li><p>No <code>fastboot devices</code> problems</p>
</li>
<li><p>No bootloops</p>
</li>
<li><p>Just a clean, smooth downgrade</p>
</li>
</ul>
<hr />
<h2 id="heading-back-on-android-14-fully-stable">✅ Back on Android 14 – Fully Stable</h2>
<p>I’m now back on <strong>Android 14</strong>, with a stable build that works perfectly with:</p>
<ul>
<li><p><strong>CameraFI Live</strong></p>
</li>
<li><p><strong>RustDesk screen sharing</strong></p>
</li>
<li><p><strong>MacroDroid automation</strong></p>
</li>
<li><p><strong>Powerbank-powered long streams (15–18 hours)</strong></p>
</li>
</ul>
<p>No permission glitches, no crashes, no battery optimization surprises.</p>
<hr />
<h2 id="heading-final-note">💡 Final Note</h2>
<p>If you're in a similar situation, don’t waste time manually unpacking payloads or hoping for fastboot images — just grab the full OTA zip from: 👉 <a target="_blank" href="https://community.oneplus.com/thread/1733980920295194629">OnePlus OTA Archive Thread</a></p>
<p>It saved me hours — and now my streaming setup is <strong>back to being bulletproof.</strong></p>
]]></content:encoded></item><item><title><![CDATA[Dealing with Unsolicited Calls from Banks: Guidelines from TRAI and RBI]]></title><description><![CDATA[Receiving unsolicited calls from banks can be frustrating and even harassing for consumers. Fortunately, there are guidelines in place from regulatory bodies such as the Telecom Regulatory Authority of India (TRAI) and the Reserve Bank of India (RBI)...]]></description><link>https://blog.svarun.dev/dealing-with-unsolicited-calls-from-banks-guidelines-from-trai-and-rbi</link><guid isPermaLink="true">https://blog.svarun.dev/dealing-with-unsolicited-calls-from-banks-guidelines-from-trai-and-rbi</guid><category><![CDATA[trai]]></category><category><![CDATA[RBI]]></category><category><![CDATA[banking]]></category><category><![CDATA[Complaints]]></category><category><![CDATA[Consumer rights]]></category><dc:creator><![CDATA[Varun Sridharan]]></dc:creator><pubDate>Tue, 21 Feb 2023 04:55:16 GMT</pubDate><content:encoded><![CDATA[<p>Receiving unsolicited calls from banks can be frustrating and even harassing for consumers. Fortunately, there are guidelines in place from regulatory bodies such as the Telecom Regulatory Authority of India (TRAI) and the Reserve Bank of India (RBI) to protect consumers' rights.</p>
<p>According to the TRAI, telemarketers are not allowed to make calls to a customer who has registered on the National Do Not Call (DND) registry. Additionally, customers who receive unsolicited calls can file a complaint with their telecom operator, who is responsible for ensuring that the customer's privacy is protected. Customers can also register a complaint directly with the TRAI at <a target="_blank" href="mailto:daca@trai.gov.in">daca@trai.gov.in</a></p>
<p>The RBI has also issued guidelines for banks regarding personal loans and credit cards. When a customer declines an offer for a personal loan or credit card, the bank is not allowed to force the customer to take the loan or card. If the bank continues to harass the customer with unsolicited calls or messages, the customer can file a complaint with the bank's customer care department. If the issue is not resolved, the customer can file a complaint with the RBI's Consumer Education and Protection Department at <a target="_blank" href="mailto:crpc@rbi.org.in">crpc@rbi.org.in</a>.</p>
<p>It is also important to note that customers have legal rights in such cases. According to Section 509 of the Indian Penal Code, any person who intends to insult the modesty of a woman or makes any unwelcome sexual advances can be punished with imprisonment or a fine. In the case of unsolicited calls from banks, customers can file a police complaint if they feel that the calls constitute harassment or intimidation.</p>
<h3 id="heading-steps-to-protect-your-rights">Steps to protect your rights :</h3>
<p>If you are receiving unsolicited calls from a bank, you can take the following steps to protect your rights:</p>
<ol>
<li><p>Register on the National Do Not Call (DND) registry to prevent telemarketers from calling you.</p>
</li>
<li><p>File a complaint with your telecom operator or directly with the TRAI at <a target="_blank" href="mailto:daca@trai.gov.in">daca@trai.gov.in</a></p>
</li>
<li><p>File a complaint with the bank's customer care department</p>
</li>
<li><p>If the issue is not resolved, file a complaint with the RBI's Consumer Education and Protection Department at <a target="_blank" href="mailto:crpc@rbi.org.in">crpc@rbi.org.in</a></p>
</li>
<li><p>Consider filing a police complaint if the calls constitute harassment or intimidation</p>
</li>
</ol>
<h3 id="heading-how-i-was-affected">How I Was Affected</h3>
<p>The constant barrage of phone calls from banks offering personal loans had a significant impact on my daily life. As I mentioned earlier, I received 5-7 calls per day from various private banks such as HDFC, IndusInd, and Axis Bank, despite repeatedly telling them that I was not interested in their offers.</p>
<p>The calls would come in at all hours of the day, even when I was working on something important or driving somewhere. The constant interruptions were highly distracting and stressful, and made it difficult to focus on anything else.</p>
<p>As the months went by, the situation became increasingly frustrating and demoralizing. It felt like the banks were ignoring my requests and forcing their services upon me, which only added to my stress levels.</p>
<p>In addition to the impact on my daily life, the repeated phone calls also caused me to lose trust in the banking system. I began to question how my personal information was being used, and whether these banks were truly looking out for my best interests as a customer.</p>
<p>Overall, the experience was highly negative and had a significant impact on my mental health and overall well-being. It's important for banks to understand the impact their actions can have on their customers, and to take steps to ensure that they are not causing undue stress or harm.</p>
]]></content:encoded></item><item><title><![CDATA[Install Alpine Linux In A Virtual Machine]]></title><description><![CDATA[Alpine Linux is super lightweight and its easy for developer who use Docker to generate their own images using alpine as base. but for me i wanted to run some stuff via cron and for which i was not interested to install Ubuntu  as those are very reso...]]></description><link>https://blog.svarun.dev/install-alpine-linux-in-a-virtual-machine</link><guid isPermaLink="true">https://blog.svarun.dev/install-alpine-linux-in-a-virtual-machine</guid><category><![CDATA[operating system]]></category><category><![CDATA[Docker]]></category><category><![CDATA[networking]]></category><category><![CDATA[learning]]></category><dc:creator><![CDATA[Varun Sridharan]]></dc:creator><pubDate>Mon, 12 Apr 2021 10:48:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1618223296948/SPSeJvsi5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Alpine Linux is super lightweight and its easy for developer who use Docker to generate their own images using alpine as base. but for me i wanted to run some stuff via cron and for which i was not interested to install <strong>Ubuntu</strong>  as those are very resource hungry.</p>
<h2 id="installing-alpine-os">Installing Alpine OS</h2>
<h4 id="step-1">Step 1</h4>
<p>Download any alpine linux https://www.alpinelinux.org/downloads/ - i used alpine server (extended)</p>
<h4 id="step-2">Step 2</h4>
<p>Create A VM With min config in FreeNAS</p>
<h4 id="step-3">Step 3</h4>
<p>Boot into VM With VNC / Serial in FreeNAS</p>
<h4 id="step-4">Step 4</h4>
<p>Login as root -- No Password Required</p>
<h4 id="step5">Step5</h4>
<p>Type <code>setup-alpine</code></p>
<ol>
<li>Select your keyboard layout. i used <code>us</code> and <code>us-alt-intl</code></li>
<li>Enter Custom Hostname if required</li>
<li>Select The Network Port</li>
<li>Select IP Type (DHCP / Static)</li>
<li>Set ROOT Password</li>
<li>Set Timezone</li>
<li>Follow Any Other Instructions</li>
<li>for download mirror use type <code>f</code> to auto select</li>
<li>Install SSH Server if required</li>
<li>Select The Disk you want to install in eg (sda) and set it to sys to install it</li>
<li>Remove Install Media And Reboot</li>
</ol>
<p>Thats it alpine OS is now installed in that VM.</p>
<hr />
<h2 id="installing-docker-in-alpine">Installing Docker In Alpine</h2>
<h4 id="step-1">Step 1</h4>
<p>run <code>apk add docker</code> Docker package is available in the Community repository. 
Therefore, if apk add fails because of unjustifiable constraints error, 
you need to edit the <code>/etc/apk/repositories</code> file to add (or comment) a line. Community repository link: http://dl-cdn.alpinelinux.org/alpine/latest-stable/community.</p>
<h4 id="step-2">Step 2</h4>
<p>run <code>apk update &amp;&amp; apk add docker</code> again if repositories file edited </p>
<h4 id="step-3">Step 3</h4>
<ul>
<li>To start the Docker daemon at boot, run <code>rc-update add docker boot</code></li>
<li>Then to start the Docker daemon manually run <code>service docker start</code></li>
</ul>
<hr />
<h2 id="installing-docker-compose">Installing Docker Compose</h2>
<h4 id="step-1">Step 1</h4>
<p>run <code>apk add py-pip</code> it will install Python &amp; PIP</p>
<h4 id="step-2">Step 2</h4>
<p><strong>Since docker-compose version 1.24.0, you also need some dev dependencies:</strong>
<code>apk add python-dev libffi-dev openssl-dev gcc libc-dev make</code></p>
<h4 id="step-3">Step 3</h4>
<p><strong>Then install docker-compose, run:</strong>
<code>pip install docker-compose</code></p>
<hr />
<h3 id="wrapping-up">👀 Wrapping Up</h3>
<p>Do not hesitate to share your feedback</p>
<p>I am on Twitter <a href="https://sva.one/twitter">varunsridharan2</a>. Give a follow!</p>
<p>Follow me on Github <a href="https://sva.one/github">varunsridharan</a>, Don't miss my amazing projects! 💯</p>
<h3 id="lets-connect">🌎 Lets connect</h3>
<ul>
<li><a target="_blank" href="https://sva.one/github">Github</a></li>
<li><a target="_blank" href="https://sva.one/twitter">Twitter</a></li>
<li><a target="_blank" href="https://sva.one/instagram">Instagram</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Simple MySQL Backup Script In Shell Script]]></title><description><![CDATA[This post will be relatively short compared to my other posts and may not be informative as my other posts too. I am writing this blog just to share a script that I have been using for years to take the backup of MYSQL database.
Requirements
Please m...]]></description><link>https://blog.svarun.dev/simple-mysql-backup-script-in-shell-script</link><guid isPermaLink="true">https://blog.svarun.dev/simple-mysql-backup-script-in-shell-script</guid><category><![CDATA[MySQL]]></category><category><![CDATA[Backup]]></category><category><![CDATA[DevLife]]></category><category><![CDATA[Databases]]></category><dc:creator><![CDATA[Varun Sridharan]]></dc:creator><pubDate>Mon, 30 Nov 2020 13:29:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1606742846573/18Dg1CYHp.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This post will be relatively short compared to my other posts and may not be informative as my other posts too. I am writing this blog just to share a script that I have been using for years to take the backup of <strong><em>MYSQL</em></strong> database.</p>
<h2 id="requirements">Requirements</h2>
<p>Please make sure you have installed <code>MySQL</code> &amp; <code>mysqldump</code> program if you are running this script from a remote server. </p>
<p>if you are using it on the same server then no additional programs need to be installed. </p>
<h2 id="script">Script</h2>
<pre><code class="lang-shell">#!/bin/sh

# SQL Export File Name
FILE=db.`date +"%Y%m%d-%H%M%S"`.sql

# Database Server Host Name
DBSERVER="localhost"  

# Database Name
DATABASE=""

# Database User Name
USER=""

# Database User Password
PASS=""

# Backup Script
mkdir /root/backup/mysql/

cd /root/backup/mysql/ 

mysqldump --opt --user=${USER} --password=${PASS} --host=${DBSERVER} ${DATABASE} &gt; ${FILE}

echo "SQL Backup ${FILE} was created:"
</code></pre>
<h2 id="feedback">Feedback</h2>
<p>I have been using this for years now and have never faced any issues to date and this script runs mostly via CRON in my server which triggers this script every day.</p>
<p>I have tested and actually used this script with a database that is over 1GB in-size too. never faced issues in exporting it.</p>
<hr />
<div class="hn-embed-widget" id="blog-footer"></div>]]></content:encoded></item><item><title><![CDATA[Configure Reverse Proxy In cPanel]]></title><description><![CDATA[Setting up a reverse proxy or a regular CentOS server is quite straight forward, however when you have cPanel included in the equation, you need to be aware of a few things. That is why I've decided to write this short post here.
Here is what you nee...]]></description><link>https://blog.svarun.dev/configure-reverse-proxy-in-cpanel</link><guid isPermaLink="true">https://blog.svarun.dev/configure-reverse-proxy-in-cpanel</guid><category><![CDATA[Developer Tools]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[apache]]></category><dc:creator><![CDATA[Varun Sridharan]]></dc:creator><pubDate>Sun, 29 Nov 2020 11:42:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1606650065453/xaBguCy7V.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Setting up a reverse proxy or a regular CentOS server is quite straight forward, however when you have cPanel included in the equation, you need to be aware of a few things. That is why I've decided to write this short post here.</p>
<p>Here is what you need to do in order to get the reverse proxy working on a cPanel server:</p>
<h2 id="prerequisites">Prerequisites</h2>
<ol>
<li>cPanel Server</li>
<li>Apache Modules<ol>
<li><a target="_blank" href="http://httpd.apache.org/docs/current/mod/mod_proxy.html">mod_proxy</a> <strong><em>-- Required</em></strong></li>
<li><a target="_blank" href="https://httpd.apache.org/docs/2.4/mod/mod_proxy_http.html">mod_proxy_http</a> <strong><em>-- Required</em></strong></li>
<li><a target="_blank" href="https://httpd.apache.org/docs/2.4/mod/mod_proxy_connect.html">mod_proxy_connect</a> <strong><em>-- Required</em></strong></li>
<li><a target="_blank" href="https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html">mod_proxy_wstunnel</a> <em>-- Optional. enable if you want to use WebSockets</em></li>
</ol>
</li>
</ol>
<h2 id="lets-get-started">Lets Get Started</h2>
<h3 id="setup-apache-modules">Setup Apache Modules</h3>
<h4 id="login-to-whm-and-navigate-to-home-software-easyapache-4">Login to WHM and navigate To <code>Home » Software » EasyApache 4</code></h4>
<p><img src="https://s2.do-spaces.com/2020/Nov/29/160664814488.jpg" alt="Easy Apache Menu" /></p>
<h4 id="click-customize-on-currently-installed-packages">Click <code>Customize</code> On <strong>Currently Installed Packages</strong></h4>
<p><img src="https://s2.do-spaces.com/2020/Nov/29/160664821549.jpg" alt="Customize Button" /></p>
<h4 id="once-it-loaded-click-apache-modules-in-the-left-side-menu">Once it loaded click <code>Apache Modules</code> In the Left-side Menu</h4>
<p><img src="https://s2.do-spaces.com/2020/Nov/29/16066482864.jpg" alt="Apache Modules Menu" /></p>
<h4 id="select-all-required-proxy-modules-that-mentioned-above">Select All <code>REQUIRED</code> Proxy Modules That Mentioned Above</h4>
<p><img src="https://s2.do-spaces.com/2020/Nov/29/16066488073.jpg" alt="Modules" /></p>
<p>Once you have selected the modules click <strong>Review</strong> in the left menu.</p>
<p>And click <strong>Provision</strong> To install &amp; Setup the apache modules.</p>
<hr />
<h3 id="creating-a-reverse-proxy">Creating A Reverse Proxy</h3>
<p>Create a new file if not already exists in the below locations</p>
<p><strong><em>STD</em></strong> -- Runs Without SSL</p>
<pre><code>/usr/<span class="hljs-keyword">local</span>/apache/conf/userdata/ssl/<span class="hljs-number">2</span>_4/{<span class="hljs-keyword">user</span>}/yourdomain.com/proxy_pass.conf
</code></pre><p><strong><em>SSL</em></strong> -- Runs With SSL</p>
<pre><code>/usr/<span class="hljs-keyword">local</span>/apache/conf/userdata/std/<span class="hljs-number">2</span>_4/{<span class="hljs-keyword">user</span>}/yourdomain.com/proxy_pass.conf
</code></pre><blockquote>
<p>(change '{user}' with the actual cPanel username')</p>
</blockquote>
<p>Enable custom VHost file by running the below cmd</p>
<pre><code>/scripts/ensure_vhost_includes <span class="hljs-comment">--user={user}</span>
</code></pre><p> After that you can then add the custom rules at the custom Vhost files which we created before.</p>
<p>Add below config to those VHosts file. which will forword all the requests for a domain to the given IP and also allows cPanel to handle SSL if it uses WEB ROOT to fetch SSL</p>
<pre><code>###### <span class="hljs-keyword">DO</span> <span class="hljs-keyword">NOT</span> REMOVE BELOW <span class="hljs-type">LINE</span>. IT USED <span class="hljs-keyword">TO</span> AUTO RENEW SSL VIA CPANEL ######
ProxyPass "/.well-known"  !
###### <span class="hljs-keyword">DO</span> <span class="hljs-keyword">NOT</span> REMOVE ABOVE <span class="hljs-type">LINE</span>. IT USED <span class="hljs-keyword">TO</span> AUTO RENEW SSL VIA CPANEL ######

ProxyPass "/"  "http://10.0.3.2:80/"
ProxyPassReverse "/"  "http://10.0.3.2:80/"
</code></pre><p>Next step is to make sure that the written config is valid. run the below cmd</p>
<pre><code><span class="hljs-attribute">service</span> httpd configtest
</code></pre><p>If you get '<code>Syntax OK</code>' then run the below cmds to rebuild apache config &amp; restart the Apache service</p>
<pre><code>/scripts/rebuildhttpdconf 

service httpd <span class="hljs-keyword">restart</span>
</code></pre><p>This pretty much it. All of this is required because you would lose all of your changes if you add them directly to your main httpd conf file.</p>
<hr />
<p>I have also create a simple script which can handle creation of reverse proxy config in cPanel </p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/varunsridharan/cpanel-apache-proxy">https://github.com/varunsridharan/cpanel-apache-proxy</a></div>
<hr />
<div class="hn-embed-widget" id="blog-footer"></div>]]></content:encoded></item><item><title><![CDATA[Convert Putty Key Gen File Into RSA Key File]]></title><description><![CDATA[As a windows user i generate my SSH keys using Putty Key Generator which provides the key in .ppk. but today i had a special requirement which required my SSH Key in RSA Format
Since i learned something new on how to convert .ppk into RSA i thought t...]]></description><link>https://blog.svarun.dev/convert-putty-key-gen-file-into-rsa-key-file</link><guid isPermaLink="true">https://blog.svarun.dev/convert-putty-key-gen-file-into-rsa-key-file</guid><category><![CDATA[Developer Tools]]></category><category><![CDATA[DevLife]]></category><category><![CDATA[sysadmin]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Varun Sridharan]]></dc:creator><pubDate>Sat, 28 Nov 2020 12:39:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1606567164980/xKEq5HhcC.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As a windows user i generate my SSH keys using <code>Putty Key Generator</code> which provides the key in <code>.ppk</code>. but today i had a special requirement which required my <strong>SSH</strong> Key in <strong><em>RSA</em></strong> Format</p>
<p>Since i learned something new on how to convert <code>.ppk</code> into <code>RSA</code> i thought that i could share it with you to.</p>
<h2 id="lets-get-started">Lets Get Started</h2>
<h3 id="requirements">Requirements</h3>
<ol>
<li>Putty Generated Key</li>
<li>Putty Key Generator App For Windows</li>
<li>Linux Server (Ubuntu preferred)</li>
</ol>
<h3 id="step-1">Step 1</h3>
<p>Load the <strong>Private Key File</strong> in Putty Key Generator and export it as OpenSSH Key</p>
<p><img src="https://s2.do-spaces.com/2020/Nov/28/160656658160.jpg" alt="Image" /></p>
<p>The exported key file should have text "BEGIN OPENSSH PRIVATE KEY"
<img src="https://s2.do-spaces.com/2020/Nov/28/160656672478.jpg" alt="Exported File" /></p>
<h3 id="step-2">Step 2</h3>
<p>Upload the file to a Linux server and run the below cmd which will convert the OpenSSH Key into RSA and update the same file</p>
<pre><code>ssh-keygen -p -m PEM -f  {your-key-filename}
</code></pre><h3 id="step-3">Step 3</h3>
<p>Thats it. your putty Generated key file is now converted to RSA key. </p>
<hr />
<div class="hn-embed-widget" id="blog-footer"></div><hr />
]]></content:encoded></item><item><title><![CDATA[Auto Organize CCTV Recordings Using Bash]]></title><description><![CDATA[In my home i am running a NVR (Network Attached Video Recorder) which captures & stores all the CCTV recordings in my storage server. but the files that is created by the recorder is not well organized and it looks something like below
2020-11-27T16-...]]></description><link>https://blog.svarun.dev/auto-organize-cctv-recordings-using-bash</link><guid isPermaLink="true">https://blog.svarun.dev/auto-organize-cctv-recordings-using-bash</guid><category><![CDATA[Bash]]></category><category><![CDATA[automation]]></category><category><![CDATA[DevLife]]></category><category><![CDATA[Developer]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Varun Sridharan]]></dc:creator><pubDate>Fri, 27 Nov 2020 13:11:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1606482548638/G41i2sxv_.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In my home i am running a <strong>NVR</strong> (<code>Network Attached Video Recorder</code>) which captures &amp; stores all the <strong><em>CCTV</em></strong> recordings in my storage server. but the files that is created by the recorder is not well organized and it looks something like below</p>
<pre><code class="lang-bash">2020-11-27T16-01-40.mp4
2020-11-27T16-06-18.mp4
2020-11-27T16-10-29.mp4
2020-11-27T16-14-05.mp4
2020-11-27T16-15-04.mp4
</code></pre>
<p>you can see the above file name is a actually a <strong><em>Time Stamp</em></strong> and it can be easy to identify the file when we don't have large no of files. </p>
<p>It will become hard when we need to look for a specific recording for a date &amp; time.</p>
<blockquote>
<p>So i decided to write a script which can be automated using cron to organize the files.</p>
</blockquote>
<h2 id="how-it-should-be-organized">How it should be organized ?</h2>
<p>I wanted this to be quickly accessible at any cost and the folder structure that i choose is below and i am sure this is the most common structure too</p>
<pre><code>/{year}/{month}/{<span class="hljs-type">date</span>}-{short-day-<span class="hljs-type">name</span>}/{hour}-{minutes}-{seconds}.mp4

Example : /<span class="hljs-number">2020</span>/<span class="hljs-number">01</span>/<span class="hljs-number">01</span>-Wed/<span class="hljs-number">13</span><span class="hljs-number">-04</span><span class="hljs-number">-00.</span>mp4
</code></pre><h2 id="why-i-chose-bash">Why i chose BASH ?</h2>
<p>Mainly because i don't have to install any additional software for it &amp; i wanted to try BASH with a real-world issue </p>
<h2 id="lets-create-the-script">Let's Create The Script</h2>
<blockquote>
<p>for the purpose of this blog post my recording path will be <code>/mnt/cctv/cam1/</code></p>
</blockquote>
<p>Create a file named <code>organizer.sh</code> and <code>#!/bin/bash</code> in the first line.</p>
<p>First we need to find all the files &amp; directory inside the recording folder for which we can use <code>*</code> at the end of the path which  will provide the full list of files &amp; directory inside <strong>cam1</strong> folder and this should be used with <code>for</code> loop so that we can loop over all the file names.</p>
<pre><code class="lang-bash"><span class="hljs-keyword">for</span> entry <span class="hljs-keyword">in</span> /mnt/cctv/cam1/*; <span class="hljs-keyword">do</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">${entry}</span>"</span>
<span class="hljs-keyword">done</span>
</code></pre>
<p>Since it lists everything inside a directory we need to make sure that we are looping just for the filenames. so its better to check if its a directory or not. using <code>! -d $entry</code> will check if the given path is not a directory</p>
<pre><code class="lang-bash"><span class="hljs-keyword">if</span> [ ! -d <span class="hljs-variable">$entry</span> ]; <span class="hljs-keyword">then</span> 
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">${entry}</span> is not a directory"</span>
<span class="hljs-keyword">fi</span>
</code></pre>
<p>when you run the above code it will provide the full path of a file instead of just the file name which  will look like below.</p>
<pre><code class="lang-bash=">/mnt/cctv/cam1/2020-11-27T16-01-40.mp4
/mnt/cctv/cam1/2020-11-27T16-06-18.mp4
/mnt/cctv/cam1/2020-11-27T16-10-29.mp4
/mnt/cctv/cam1/2020-11-27T16-14-05.mp4
/mnt/cctv/cam1/2020-11-27T16-15-04.mp4
</code></pre>
<p>so we need to extract the file name from the path string using <code>basename</code> function in <code>bash</code></p>
<pre><code class="lang-bash">FILE_BASE_NAME=<span class="hljs-string">"<span class="hljs-subst">$(basename <span class="hljs-string">"<span class="hljs-variable">$entry</span>"</span>)</span>"</span>
</code></pre>
<p>which set the value of <code>FILE_BASE_NAME</code> as <code>2020-11-27T16-01-40.mp4</code> instead of the full path.</p>
<p>Next step is to extract time stamp &amp; file extension which can be done by split the string into 2 using <code>tr</code> function.</p>
<pre><code class="lang-bash">FILE_DATE_TIME_RAW=($(<span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">$FILE_BASE_NAME</span>"</span> | tr <span class="hljs-string">"."</span> <span class="hljs-string">"\n"</span>))
</code></pre>
<blockquote>
<p>Using <code>$FILE_DATE_TIME_RAW[0]</code> will provide the timestamp <code>2020-11-27T16-01-40</code></p>
<p>Using <code>$FILE_DATE_TIME_RAW[1]</code> will provide the file extension <code>mp4</code></p>
</blockquote>
<p>Next step is to split the timestamp into 2 since we just need to extract the <code>date</code>,<code>month</code> &amp; <code>year</code></p>
<pre><code class="lang-bash">FILE_DATE_TIME=($(<span class="hljs-built_in">echo</span> <span class="hljs-variable">${FILE_DATE_TIME_RAW[0]}</span> | tr <span class="hljs-string">"T"</span> <span class="hljs-string">"\n"</span>))
</code></pre>
<blockquote>
<p>Using <code>$FILE_DATE_TIME[0]</code> will provide the date stamp <code>2020-11-27</code></p>
<p>Using <code>$FILE_DATE_TIME[1]</code> will provide the time stamp <code>16-01-40</code></p>
</blockquote>
<p>Next step is the convert the date stamp string into machine readable string so that we can extract <code>date</code>,<code>month</code> &amp; <code>year</code></p>
<p>Since my storage server runs on <strong><em>FREEBSD</em></strong> i will be using </p>
<pre><code class="lang-bash">CUSTOM_FORMAT=$(<span class="hljs-built_in">echo</span> $(date -jf <span class="hljs-string">"%Y-%m-%d"</span> <span class="hljs-string">"<span class="hljs-variable">${FILE_DATE}</span>"</span> +<span class="hljs-string">"%Y/%b/%d-%a"</span> ))
</code></pre>
<p>if you are running on <strong><em>debian</em></strong> then the below code will work</p>
<pre><code class="lang-bash">CUSTOM_FORMAT=$(<span class="hljs-built_in">echo</span> $(date +<span class="hljs-string">"%Y/%b/%d-%a"</span> -d <span class="hljs-string">"<span class="hljs-variable">${FILE_DATE}</span> <span class="hljs-variable">${FILE_TIME}</span>"</span>))
</code></pre>
<blockquote>
<p>I am not sure why the <code>debian</code> code did not work for me in <code>freebsd</code> i am new to bash and learning every day!</p>
</blockquote>
<p>The final step is to make sure the output folder is created for which we need to use <code>mkdir</code> and then copy the file to new folder</p>
<pre><code class="lang-bash">FULL_SAVE_PATH=<span class="hljs-string">"/mnt/cctv-output/cam1/<span class="hljs-variable">${CUSTOM_FORMAT}</span>/<span class="hljs-variable">${FILE_DATE_TIME[1]}</span>.<span class="hljs-variable">${FILE_EXT}</span>"</span>
mkdir -p <span class="hljs-string">"/mnt/cctv-output/cam1/<span class="hljs-variable">${CUSTOM_FORMAT}</span>/"</span>
mv <span class="hljs-string">"<span class="hljs-variable">$entry</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$FULL_SAVE_PATH</span>"</span>
</code></pre>
<p>And here is the full script </p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>

SAVE_TO_PATH=<span class="hljs-string">"/mnt/cctv-output/cam1/"</span>
DIR_TO_USE=<span class="hljs-string">"/mnt/cctv/cam1/"</span>

<span class="hljs-keyword">for</span> entry <span class="hljs-keyword">in</span> <span class="hljs-string">"<span class="hljs-variable">${DIR_TO_USE}</span>"</span>/*; <span class="hljs-keyword">do</span>
    <span class="hljs-keyword">if</span> [ ! -d <span class="hljs-variable">$entry</span> ]; <span class="hljs-keyword">then</span> 
        FILE_BASE_NAME=<span class="hljs-string">"<span class="hljs-subst">$(basename <span class="hljs-string">"<span class="hljs-variable">$entry</span>"</span>)</span>"</span>
        FILE_DATE_TIME_RAW=($(<span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">$FILE_BASE_NAME</span>"</span> | tr <span class="hljs-string">"."</span> <span class="hljs-string">"\n"</span>))
        FILE_DATE_TIME=($(<span class="hljs-built_in">echo</span> <span class="hljs-variable">${FILE_DATE_TIME_RAW[0]}</span> | tr <span class="hljs-string">"T"</span> <span class="hljs-string">"\n"</span>))
        FILE_EXT=<span class="hljs-string">"<span class="hljs-variable">${FILE_DATE_TIME_RAW[1]}</span>"</span>
        FILE_TIME=$(<span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">${FILE_DATE_TIME[1]}</span>"</span> | tr <span class="hljs-string">"-"</span> <span class="hljs-string">":"</span> )
        FILE_DATE=<span class="hljs-string">"<span class="hljs-variable">${FILE_DATE_TIME[0]}</span>"</span>

        <span class="hljs-comment">#CUSTOM_FORMAT=$(echo $(date +"%Y/%b/%d-%a" -d "${FILE_DATE} ${FILE_TIME}")) -- Worked In Ubuntu</span>
        CUSTOM_FORMAT=$(<span class="hljs-built_in">echo</span> $(date -jf <span class="hljs-string">"%Y-%m-%d"</span> <span class="hljs-string">"<span class="hljs-variable">${FILE_DATE}</span>"</span> +<span class="hljs-string">"%Y/%b/%d-%a"</span> ))
        FULL_SAVE_PATH=<span class="hljs-string">"<span class="hljs-variable">${SAVE_TO_PATH}</span>/<span class="hljs-variable">${CUSTOM_FORMAT}</span>/<span class="hljs-variable">${FILE_DATE_TIME[1]}</span>.<span class="hljs-variable">${FILE_EXT}</span>"</span>

        <span class="hljs-built_in">echo</span> <span class="hljs-string">"Local File : <span class="hljs-variable">${entry}</span> =&gt; <span class="hljs-variable">${FULL_SAVE_PATH}</span>"</span>
        mkdir -p <span class="hljs-string">"<span class="hljs-variable">${SAVE_TO_PATH}</span>/<span class="hljs-variable">${CUSTOM_FORMAT}</span>/"</span>
        mv <span class="hljs-string">"<span class="hljs-variable">$entry</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$FULL_SAVE_PATH</span>"</span>
    <span class="hljs-keyword">fi</span>
<span class="hljs-keyword">done</span>
</code></pre>
<p>Thats it the script is done when you run it will auto organize the files </p>
<pre><code>/mnt/cctv-output/cam1/2020/11/27-Fri/16-01-40.mp4
/mnt/cctv-output/cam1/2020/11/27-Fri/16-06-18.mp4
/mnt/cctv-output/cam1/2020/11/27-Fri/16-10-29.mp4
/mnt/cctv-output/cam1/2020/11/27-Fri/16-14-05.mp4
/mnt/cctv-output/cam1/2020/11/27-Fri/16-15-04.mp4
</code></pre><hr />
<div class="hn-embed-widget" id="blog-footer"></div>]]></content:encoded></item><item><title><![CDATA[domQ - Lightweight Wrapper For VanillaJS]]></title><description><![CDATA[What ❓
domQ is an absurdly small jQuery alternative for modern browsers (IE11+) that provides jQuery-style syntax for manipulating the DOM. Utilizing modern browser features to minimize the code base, developers can use the familiar chain-able method...]]></description><link>https://blog.svarun.dev/domq-lightweight-wrapper-for-vanillajs</link><guid isPermaLink="true">https://blog.svarun.dev/domq-lightweight-wrapper-for-vanillajs</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Javascript library]]></category><category><![CDATA[jQuery]]></category><category><![CDATA[2Articles1Week]]></category><category><![CDATA[vanilla-js]]></category><dc:creator><![CDATA[Varun Sridharan]]></dc:creator><pubDate>Thu, 26 Nov 2020 10:54:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1606387441286/7P6uPn1AE.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="what">What ❓</h2>
<p><a target="_blank" href="https://github.com/domq-js/core">domQ</a> is an absurdly small jQuery alternative for modern browsers (IE11+) that provides jQuery-style syntax for manipulating the DOM. Utilizing modern browser features to minimize the code base, developers can use the familiar chain-able methods at a fraction of the file size. 100% feature parity with jQuery isn't a goal, but <a target="_blank" href="https://github.com/domq-js/core">domQ</a> comes helpfully close, covering most day to day use cases.</p>
<h2 id="why">Why 🤔</h2>
<blockquote>
<p>  I was very bored with jQuery script and wanted to try something else 🙈.</p>
</blockquote>
<p>Well as a web developer i wanted to move out from jQuery and use Vanilla JS. When i realized i had to write multiple lines of code for a single action thats when i decided to create a lightweight JavaScript library that can do most things that jQuery can.</p>
<blockquote>
<p><a target="_blank" href="https://github.com/domq-js/core">domQ</a> Uses <a target="_blank" href="https://github.com/varunsridharan/dizzle">Dizzle</a> which is an alternate to jQuery's Sizzle Framework which is used for Customized CSS Selectors like <code>:input</code>, <code>:hidden</code> and <a target="_blank" href="https://github.com/varunsridharan/dizzle">Dizzle</a> is bundled along with <a target="_blank" href="https://github.com/domq-js/core">domQ</a>. we both standalone &amp; bundled versions.</p>
</blockquote>
<h2 id="comparison">Comparison</h2>
<table>
<thead>
<tr>
<td>Size</td><td>domQ</td><td>domQ + Dizzle</td><td>Zepto</td><td>jQuery Slim</td></tr>
</thead>
<tbody>
<tr>
<td>Unminified</td><td>45.7 KB</td><td>77.1 KB</td><td>58.7 KB</td><td>250 KB</td></tr>
<tr>
<td>Minified</td><td>20.5 KB</td><td>31.4 KB</td><td>26 KB</td><td>70.6 KB</td></tr>
<tr>
<td>Minified &amp; Gzipped</td><td>7.50 KB</td><td>11.1 KB</td><td>9.8 KB</td><td>24.4 KB</td></tr>
</tbody>
</table>
<table>
<thead>
<tr>
<td>Features</td><td>domq</td><td>domQ + Dizzle</td><td>Zepto</td><td>jQuery Slim</td></tr>
</thead>
<tbody>
<tr>
<td>Older Browsers</td><td>❌</td><td>❌</td><td>❌</td><td>✔</td></tr>
<tr>
<td>Modern Browsers</td><td>✔</td><td>✔</td><td>✔</td><td>✔</td></tr>
<tr>
<td>Actively Maintained</td><td>✔</td><td>✔</td><td>❌</td><td>✔</td></tr>
<tr>
<td>Namespaced Events</td><td>✔</td><td>✔</td><td>❌</td><td>✔</td></tr>
<tr>
<td>jQuery Selectors</td><td>✔</td><td>✔</td><td>⚠️(Experimental Feature)</td><td>✔</td></tr>
<tr>
<td>** Animation</td><td>✔</td><td>✔</td><td>⚠️(Custom Workaround) ️</td><td>❌</td></tr>
</tbody>
</table>
<p>** domQ uses <a target="_blank" href="https://github.com/web-animations/web-animations-js">WebAnimation's</a> API</p>
<p>domQ gives you a query selector, collection methods and some library methods. If you need more details about our API just check out jQuery's, while we don't implement everything that jQuery provides, everything what we do implement should be compatible with jQuery. domQ can be extended with custom methods, read how <a target="_blank" href="https://domq.sva.wiki/developer-guides/extending-domq">here</a></p>
<blockquote>
<p><a target="_blank" href="https://github.com/domq-js/core">domQ</a> is stable but we still lack documentation. !! if you need help you can contact me directly !</p>
</blockquote>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/domq-js/core">https://github.com/domq-js/core</a></div>
<hr />
<div class="hn-embed-widget" id="blog-footer"></div><hr />
]]></content:encoded></item><item><title><![CDATA[Configure Pi-Hole With DNS over TLS - [ Private DNS ]]]></title><description><![CDATA[When Google officially launched Android 9 Pie, which includes a slew of new features around digital well-being, security, and privacy. 
If you’ve poked around the network settings on your phone, you may have noticed a new settings called Private DNS ...]]></description><link>https://blog.svarun.dev/configure-pi-hole-with-dns-over-tls-private-dns</link><guid isPermaLink="true">https://blog.svarun.dev/configure-pi-hole-with-dns-over-tls-private-dns</guid><category><![CDATA[DevLife]]></category><category><![CDATA[Developer Tools]]></category><category><![CDATA[2Articles1Week]]></category><category><![CDATA[networking]]></category><dc:creator><![CDATA[Varun Sridharan]]></dc:creator><pubDate>Wed, 25 Nov 2020 11:52:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1606305042766/H8dCWUS5M.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When Google officially launched <a target="_blank" href="https://www.android.com/versions/pie-9-0/">Android 9 Pie</a>, which includes a slew of new features around digital well-being, security, and privacy. 
If you’ve poked around the network settings on your phone, you may have noticed a new settings called <strong>Private DNS Mode</strong>.</p>
<h2 id="what-is-private-dns">What is Private DNS?</h2>
<p>The actual terminology for Private DNS is either DNS over TLS or DNS over HTTPS. TLS stands for Transport Layer Security and HTTPS stands for Hypertext Transfer Protocol Secure. </p>
<blockquote>
<p>You can read more about <a target="_blank" href="https://www.cloudflare.com/learning/dns/dns-over-tls/">DNS over TLS / DNS over HTTPS</a> at <a target="_blank" href="https://www.cloudflare.com/learning/dns/dns-over-tls/">cloudflare</a></p>
</blockquote>
<h2 id="what-is-the-need-to-use-pi-hole-with-dns-over-tls">What is the need to use Pi-Hole With DNS over TLS ?</h2>
<p>Well based on my experience not all adds are getting blocked with using Pi-hole as a dns resolver for the hole network. and android some show catches the IP Address of the add's website when its not configured to run with Private DNS. </p>
<p>And another reason is when i am using with <strong><em>Mobile Internet</em></strong> i see a lots of adds. when using <strong><em>Private DNS</em></strong> its gets reduced. </p>
<blockquote>
<p>I never used Raspberry Pi  to run Pi-Hole in my local network. so i haven't tested the below cmd with Raspbian OS.</p>
</blockquote>
<h3 id="step-1">Step 1</h3>
<p>Install Nginx using the below cmd</p>
<pre><code class="lang-shell">sudo apt-get  install nginx
</code></pre>
<h3 id="step-2">Step 2</h3>
<p>DNS over TLS requires SSL so we will be using <a target="_blank" href="https://letsencrypt.org/">Let’s Encrypt</a></p>
<p>First, add the repository:</p>
<pre><code class="lang-shell">sudo add-apt-repository ppa:certbot/certbot
</code></pre>
<p>since we are using <code>Nginx</code> we need to install <code>Certbot Nginx Addon</code></p>
<pre><code class="lang-shell">sudo apt-get install -y certbot python3-certbot-nginx
</code></pre>
<h3 id="step-3">Step 3</h3>
<p>we need to generate a ssl certificate using <a target="_blank" href="https://letsencrypt.org/">Let’s Encrypt</a></p>
<p>Make sure to replace</p>
<ol>
<li><code>&lt;your-email&gt;</code> with your email ID</li>
<li><code>&lt;your-domain&gt;</code> with the domain / subdomain you choose to use</li>
</ol>
<pre><code class="lang-shell">sudo certbot  certonly --webroot -w "/var/www/html/" --preferred-challenges http -m "&lt;your-email&gt;" -d "&lt;your-domain&gt;" -n --agree-tos --no-eff-email
</code></pre>
<h3 id="step-4">Step 4</h3>
<p>Create a new directory named <code>streams</code> inside <code>/etc/nginx/</code> and create a file <code>dns-over-tls</code> inside of <code>streams</code> directory with the below content</p>
<p>make sure to replace <code>{dns_domain_name}</code> with the actual domain name you decided to use </p>
<pre><code class="lang-shell">upstream dns-servers {
    server    127.0.0.1:53;
}
server {
  listen 853 ssl; # managed by Certbot
  ssl_certificate /etc/letsencrypt/live/{dns_domain_name}/fullchain.pem; # managed by Certbot
  ssl_certificate_key /etc/letsencrypt/live/{dns_domain_name}/privkey.pem; # managed by Certbot
  ssl_protocols        TLSv1.2 TLSv1.3;
  ssl_ciphers          HIGH:!aNULL:!MD5;

  ssl_handshake_timeout    10s;
  ssl_session_cache        shared:SSL:20m;
  ssl_session_timeout      4h;
  proxy_pass dns-servers;
}
</code></pre>
<h3 id="step-5">Step 5</h3>
<p>Edit <code>/etc/nginx/nginx.conf</code> and add the below lines which tells nginx to auto include config files inside of <code>streams</code> directory</p>
<pre><code class="lang-shell">stream {
    include /etc/nginx/streams/*;
}
</code></pre>
<h3 id="step-6">Step 6</h3>
<p>Remove all the other server config which are located inside of <code>/etc/nginx/sites-available/</code> AND <code>/etc/nginx/sites-enabled/</code></p>
<pre><code class="lang-shell">sudo rm -rf /etc/nginx/sites-available/*
sudo rm -rf /etc/nginx/sites-enabled/*
sudo service nginx start
</code></pre>
<blockquote>
<p>Make sure to have <strong>TCP PORT</strong> <code>853</code> open 
now you can use the domain name in your android mobile in private dns setting.</p>
</blockquote>
<hr />
<p>I have also made a quick setup script which you can find @</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/varunsridharan/pi-hole-android-private-dns">https://github.com/varunsridharan/pi-hole-android-private-dns</a></div>
<blockquote>
<p>if you need help in setting it up. you can contact me via <a target="_blank" href="https://twitter.com/varunsridharan2">Twitter @varunsridharan2</a> i will do my best. </p>
</blockquote>
<hr />
<div class="hn-embed-widget" id="blog-footer"></div>]]></content:encoded></item><item><title><![CDATA[Pi-Hole - What is it & How To Install It ?]]></title><description><![CDATA[What is it?
Pi-Hole is a DNS sinkhole which protects your devices from loading unwanted contents without having to install any software in your devices.

Pi-Hole is an open-source software which helps ensure you are the sole person in control of your...]]></description><link>https://blog.svarun.dev/pi-hole-what-is-it-and-how-to-install-it</link><guid isPermaLink="true">https://blog.svarun.dev/pi-hole-what-is-it-and-how-to-install-it</guid><category><![CDATA[Developer Tools]]></category><category><![CDATA[2Articles1Week]]></category><category><![CDATA[networking]]></category><category><![CDATA[NetworkAutomation]]></category><dc:creator><![CDATA[Varun Sridharan]]></dc:creator><pubDate>Tue, 24 Nov 2020 09:18:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1606208633100/aiYWV0Czf.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="what-is-it">What is it?</h2>
<p><strong><a target="_blank" href="https://pi-hole.net/">Pi-Hole</a></strong> is a <a target="_blank" href="https://en.wikipedia.org/wiki/DNS_sinkhole">DNS sinkhole</a> which protects your devices from loading unwanted contents without having to install any software in your devices.</p>
<blockquote>
<p><strong><a target="_blank" href="https://pi-hole.net/">Pi-Hole</a></strong> is an open-source software which helps ensure you are the sole person in control of your privacy</p>
</blockquote>
<hr />
<h2 id="how-it-works">How it works?</h2>
<p><a target="_blank" href="https://pi-hole.net/">Pi-hole</a> functions as an internal, private DNS server for your network. For many home users, this service is already running on your router, but your router doesn’t know where advertisements are — but Pi-hole does. Pi-hole will intercept any queries for known ad-serving domains and deny them access, so ads won’t be downloaded.</p>
<p>This means websites will load a lot faster, but without advertisements. <a target="_blank" href="https://pi-hole.net/">Pi-Hole</a> also caches these queries, so responsiveness to commonly visited websites is improved. The main advantage with <a target="_blank" href="https://pi-hole.net/">Pi-Hole</a> is</p>
<ol>
<li>It can run on almost any hardware</li>
<li>Lightweight; requires just 2GB disk space &amp; 512MB Ram</li>
<li>Blocks Ads using the block list which is updated frequently</li>
<li>Blocks unwanted sites and malware</li>
<li>Blocks Telemetry Data</li>
<li>Saves Your Internet bandwidth</li>
</ol>
<hr />
<h2 id="how-to-install">How to install?</h2>
<p>Primarily <a target="_blank" href="https://pi-hole.net/">Pi-Hole</a> was designed for <a target="_blank" href="https://www.raspberrypi.org/">RaspberryPi</a>. Soon it was extended to almost every Linux distribution. It can be deployed on any cloud service provider like <a target="_blank" href="https://m.do.co/c/dd1b2e88d855">Digitalocean</a>, <a target="_blank" href="https://www.vultr.com/?ref=7410855">Vultr</a>, etc.</p>
<table>
<thead>
<tr>
<td>Distribution</td><td>Architecture</td></tr>
</thead>
<tbody>
<tr>
<td>Raspberry Pi OS</td><td>ARM</td></tr>
<tr>
<td>Ubuntu</td><td>ARM / x86_64</td></tr>
<tr>
<td>Debian</td><td>ARM / x86_64 / i386</td></tr>
<tr>
<td>Fedora</td><td>ARM / x86_64</td></tr>
<tr>
<td>CentOS</td><td>x86_64</td></tr>
</tbody>
</table>
<blockquote>
<p>I am using a <a target="_blank" href="https://m.do.co/c/dd1b2e88d855">Digitalocean</a> Droplet to demonstrate how to install it since I don't have any free hardware left to spare at home</p>
</blockquote>
<h2 id="prerequisites">Prerequisites</h2>
<ol>
<li>2GB Disk Space (4GB preferred)</li>
<li>512MB RAM</li>
<li>1 Static IP</li>
</ol>
<h2 id="installation">Installation</h2>
<p>Running the below command will get you started with the installation</p>
<p><code>curl -sSL https://install.pi-hole.net | bash</code> </p>
<p>The command will download the install script and perform some checks before installation.</p>
<h3 id="step-1">Step 1</h3>
<p>Just press <code>OK</code></p>
<p><img src="https://s2.do-spaces.com/2020/Nov/15/1605403309-196.jpg" alt="Welcome Screen" /></p>
<h3 id="step-2">Step 2</h3>
<p>Just press <code>OK</code> Again</p>
<p><img src="https://s2.do-spaces.com/2020/Nov/15/1605403325-139.jpg" alt="Free &amp; Open Source" /></p>
<h3 id="step-3">Step 3</h3>
<p>In this step, it says that <a target="_blank" href="https://pi-hole.net/">Pi-Hole</a> is a server and it requires a STATIC IP address to work properly</p>
<p><img src="https://s2.do-spaces.com/2020/Nov/15/1605403376-115.jpg" alt="Static IP Notice" /></p>
<h3 id="step-4">Step 4</h3>
<p>if you have multiple <strong>Ethernet</strong> ports and wanted to use a port other than default then you have to select your port. but for most people, it selects the default port </p>
<p><img src="https://s2.do-spaces.com/2020/Nov/15/1605403752-137.jpg" alt="Ethernet Port Config" /></p>
<blockquote>
<p>since I am running on <a target="_blank" href="https://m.do.co/c/dd1b2e88d855">Digitalocean</a> its shows 2 usable ports. and I am going to stick with the default port which is <code>eth0</code> as its directly connected to the internet and can be accessed from anywhere</p>
</blockquote>
<h3 id="step-4">Step 4</h3>
<p>Here's where you select/enter your Upstream DNS provider.</p>
<p><strong>Upstream DNS</strong> means pi-hole forwards requests to the configured provider to fetch the IP-Address if the request itself not been blocked by <a target="_blank" href="https://pi-hole.net/">Pi-Hole</a></p>
<p><img src="https://s2.do-spaces.com/2020/Nov/15/1605404168-158.gif" alt="Upstream DNS Provider" /></p>
<blockquote>
<p>I selected <strong>Cloudflare</strong>  as the provider because its faster &amp; secure</p>
</blockquote>
<h3 id="step-5">Step 5</h3>
<p>By default <a target="_blank" href="https://pi-hole.net/">Pi-Hole</a> has a built-in list of domains that should be blocked at all times. I prefer to just stick with the defaults.</p>
<p>Just press <code>OK</code></p>
<p><img src="https://s2.do-spaces.com/2020/Nov/15/1605404326-123.jpg" alt="Block List" /></p>
<h3 id="step-6">Step 6</h3>
<p>In this step, you can configure which protocol pi-hole can block ads. just press <code>ok</code> it's better to have the defaults. 
<img src="https://s2.do-spaces.com/2020/Nov/15/1605404549-189.jpg" alt="Protocols" /></p>
<h3 id="step-7">Step 7</h3>
<p>It shows the IP Address &amp; Gateway of the connected network.
make sure to note down the IP address</p>
<p><img src="https://s2.do-spaces.com/2020/Nov/15/1605404957-149.jpg" alt="IP" /></p>
<h3 id="step-8">Step 8</h3>
<p>Just Press <code>ok</code> but make sure to assign a static IP in the router level if you are installing it in your local network. that why the same IP will not be assigned to any other devices. </p>
<p><img src="https://s2.do-spaces.com/2020/Nov/15/1605405024-146.jpg" alt="Conflict" /></p>
<h3 id="step-9">Step 9</h3>
<p>Asks if you need an admin interface running on a web server. 
It's better to have. As it makes it easy to configure <a target="_blank" href="https://pi-hole.net/">Pi-Hole</a>.</p>
<p><img src="https://s2.do-spaces.com/2020/Nov/15/1605405129-197.jpg" alt="Need Web Admin ?" /></p>
<h3 id="step-10">Step 10</h3>
<p>By default <a target="_blank" href="https://pi-hole.net/">Pi-Hole</a> will provide with <code>Lighttpd</code> which is great. if you have any other web server running then you can choose <strong>no</strong> 
as I don't have any. I am selecting <strong>Yes</strong></p>
<p><img src="https://s2.do-spaces.com/2020/Nov/15/1605405262-169.jpg" alt="Web Server" /></p>
<h3 id="step-11">Step 11</h3>
<p>Select <code>yes</code> if you want to see which device quires which website. basically, logs every request made by a device.
I prefer to select <code>yes</code> because it provides a list and I can quickly check and block any wanted 
<img src="https://s2.do-spaces.com/2020/Nov/15/1605405390-177.jpg" alt="Log Quires" /></p>
<h3 id="step-12-final-step">Step 12 -- Final Step</h3>
<p>This is to customize how the logs should be saved. just press <code>ok</code> and want for a few minutes to get this installed
<img src="https://s2.do-spaces.com/2020/Nov/15/1605405665-19.jpg" alt="Logs" /></p>
<h3 id="finished">Finished</h3>
<p>This is the final screen which shows some important information. such as</p>
<ol>
<li>IP Address</li>
<li>Admin Password</li>
<li>Admin Page URL</li>
</ol>
<p>Make sure write them down</p>
<p><img src="https://s2.do-spaces.com/2020/Nov/15/1605409240-125.jpg" alt="Finished" /></p>
<p>Once you are logged into the admin panel you will see something like below</p>
<p><img src="https://s2.do-spaces.com/2020/Nov/15/1605409363-123.jpg" alt="Pi-Hole Admin" /></p>
<hr />
<p>You can find more information about setting up Pi-hole on the <a target="_blank" href="https://github.com/pi-hole/pi-hole/">GitHub repository</a>.</p>
<p>If you need support with using Pi-hole or want to chat with the Pi-hole community, you can visit their <a target="_blank" href="https://discourse.pi-hole.net/">forum here</a>.</p>
<hr />
<h2 id="honest-feedback">Honest Feedback</h2>
<p>In my family, I am the only person who can actually differentiate between genuine content/advertisement but other members of my family including my parents can't able to understand which is an advertisement. and in mobile everything is cluttered with adds every app that you installed has advertisement which is irrelevant &amp;  inappropriate. </p>
<p>That's when I decided to use <a target="_blank" href="https://pi-hole.net/">Pi-Hole</a> 2 years ago. and got to say it really made a big difference. it did the best to block all irrelevant &amp;  inappropriate advertisement in the whole local network. and it saved me almost 20% of my bandwidth usage.</p>
<p>I urge people to give it a try and see the actual difference.</p>
<blockquote>
<p>if you need help in setting it up. you can contact me via <a target="_blank" href="https://twitter.com/varunsridharan2">Twitter @varunsridharan2</a> i will do my best. </p>
</blockquote>
<hr />
<div class="hn-embed-widget" id="blog-footer"></div>]]></content:encoded></item><item><title><![CDATA[Hard Lesson: The non-negotiable importance of a healthy work-life balance]]></title><description><![CDATA[I am a 26-year old software developer from Chennai, India. I have always been more of a self-taught person than a mainstream academic. My interest in technology and development stemmed from this passion for self-learning. This interest prompted me to...]]></description><link>https://blog.svarun.dev/hard-lesson-the-non-negotiable-importance-of-a-healthy-work-life-balance</link><guid isPermaLink="true">https://blog.svarun.dev/hard-lesson-the-non-negotiable-importance-of-a-healthy-work-life-balance</guid><category><![CDATA[DevLife]]></category><category><![CDATA[lifestyle]]></category><category><![CDATA[life]]></category><category><![CDATA[Story]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Varun Sridharan]]></dc:creator><pubDate>Mon, 23 Nov 2020 11:46:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1606131552880/yFcXS2PWL.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I am a 26-year old software developer from Chennai, India. I have always been more of a self-taught person than a mainstream academic. My interest in technology and development stemmed from this passion for self-learning. This interest prompted me to complete advanced diplomas in Computer and Mobile Hardware, Software, Networking, Web Designing, Web Development, Audio-Visual Editing, and Server Administration.</p>
<p>The above has been the narrative that I have stuck to all-along. However, there is more to my story, which I haven't revealed before. This blog post is an open narrative of my life, detailing the various choices I have made, which ended up wreaking havoc with my physical health and mental well-being.</p>
<p>I learnt the hard way, the need for, and importance of, a healthy work-life balance, along with consistent physical activity. A lack of this balance bordered on an unhealthy obsession with work, affecting both physical health and mental well-being. This in-turn, ironically, affected the efficiency and productivity of work itself. Looking back, I see where I have gone wrong, but I was too young to realize the path I was heading on at the time, which I regret in hindsight.</p>
<h2 id="about-me">About Me</h2>
<p><img src="https://s2.do-spaces.com/2020/Nov/23/160613113280.png" alt="About ME" /></p>
<p>My first exposure to a computer was at the age of 3. I spent most of my waking time in front of the computer, trying to disassemble it, to learn about its architecture and inner workings. Computers quickly became my passion, and quite honestly an obsession, to the point that I stopped going out to play with friends. This was the beginning of my soon-to-become-permanent sedentary and inactive lifestyle. </p>
<p>I kept thinking about computers and technology all the time, even at school. Instead of being in the classroom studying, I used to go to the computer lab and help the lab engineer. The headmaster at school called my parents and complained about my unhealthy obsession with computers and the fact that I was losing focus and interest in academics. My parents, concerned about my obsession, took me to a Psychologist. He heard me out, and tried to talk to me about the importance of academics, but I was absolutely stubborn about quitting academics and focusing full-time on computers. </p>
<p>Finally, after much persuasion and discussion, my parents realized that I was no longer inclined towards mainstream academics. They allowed me to discontinue my academics in grade 7, and start working on computers full-time. It seemed like a great victory for me, but it so ended up that I started sitting on the computer for 20-22 hours a day, getting up only to eat or sleep. This compounded an already sedentary lifestyle, and led to a host of other ailments.</p>
<h2 id="my-health-a-rocky-beginning-and-unending-concerns">My health: a rocky beginning and unending concerns</h2>
<p><img src="https://s2.do-spaces.com/2020/Nov/23/160613120325.png" alt="health" /></p>
<p>I had developed an ear infection at a very early age, which went unnoticed until I was about 14 years old. Each time the infection flared up, we went to a doctor who prescribed medication and said it would go away in a few days. Over the years we saw several doctors, each of whom followed the same course of action. </p>
<p>The infection advanced with time and was accompanied with heavy dizziness, which made it hard for me to sit or stand. At this point, my parents and I went to see a specialist, who told me that my left ear was heavily infected and would need immediate surgery. A few months after, the right ear also needed to be operated on due to bone damage from the infection, and needed a steel plate. I suffered with dizziness for a long time after the surgery too.</p>
<p>All this affected my hearing, reducing my auditory capacity to 55% in my left ear and 75% in my right. Till date, I can only hear high-decibel noises only, and have trouble hearing speech and other sounds at low-decibels.</p>
<p>After about a year, I got a bit of a skin rash. Initially it subsided with some medication, but then it became more severe and spread throughout the body. I visited a number of skin specialists who ran various tests ranging from blood count to cancer, but none provided a definite answer to the condition. The infection had spread all over the body and was deeply agonizing making it hard to sit, stand or sleep. Finally, after over two months and <strong>INR 60,000 (<em>~1000USD</em>)</strong> in tests, the issue had become evident. The over-prescription of steroid medication for the initial skin rash is what triggered this skin condition, but it was in great deal caused due to constant stress. It took a further 2 years and 6 months to partly recover from the damage, which was excruciatingly painful and depressing. Stress can be greatly damaging!</p>
<p>Further, due to my incorrect pattern of brushing and improper oral hygiene, I ended up with 8 cavities. I have had to get eight of my teeth removed and replaced with artificial teeth.</p>
<p>To add on to things, my sedentary life and lack of physical activity reduced my outdoor exposure to the sun, resulting in a vitamin D deficiency. It also caused me to become extremely obsese. At the peak, I weighed about 165 kilograms. After some physical activity and lifestyle change, I reduced my weight to 140 kilograms. This seemed like a great win, and I started neglecting physical activity and started sitting in front of the computer again. :(</p>
<p>My biggest and most challenging health concern continues to be my obesity, due to my lack of interest in a disciplined regimen of physical activity.</p>
<p>Thankfully though, I am happy that as on date I have a working mind and body :)</p>
<h2 id="lessons-learnt">Lessons learnt</h2>
<p><img src="https://s2.do-spaces.com/2020/Nov/23/160613133555.png" alt="lessons" /></p>
<p>I have learnt that academics or no academics, life is meant to be enjoyed and lived with interest, passion and love. We must not forget that our body and mind play the key role in order to achieve great things in life and to experience life as it is meant to be. We must never take them for granted.</p>
<p>Nothing must get in the way of a healthy and disciplined lifestyle with equal balance of work and life. Make time for a hobby such as reading or music. Family time is also important. Spend time with your parents, spouse, siblings and children. These activities help calm the mind and keep stress levels in control. For me, my biggest stress buster has been my dog, Sofie :)</p>
<p>Limit working hours to no more than 10-12 hours a day. Even while working, get up every hour and stretch the body. Focus the eyes away from the screen once at least every 15 minutes to avoid fatigue and dryness. No matter how long you work, there is always work to be done! It is always better to work in a systematic and disciplined manner.</p>
<p>Physical activity and fitness is absolutely essential for maintaining good health and a sharp mind. Most people lose interest in working out, because it is too boring to do alone. I would recommend a sport involving friends, so it is fun, but also meets the requirement for physical activity. It also helps gain some exposure to the sun, meeting the Vitamin D quota! ;) </p>
<p>Sleep is extremely important. Make sure to get at least 6-7 hours of good sleep to help the body and mind rejuvenate and refresh.</p>
<p>I urge those reading my blog to learn from the mistakes I have made, and the pain I have had to experience. Try and alter your choices, so you can live a life of inspiration, love, contentment and fulfillment. You can still work on whatever it is that you find passionate, but you can do it without having to compromise due to health limitations.</p>
]]></content:encoded></item><item><title><![CDATA[How to publish a Github action To Github Marketplace]]></title><description><![CDATA[You can publish the actions you have created to the GitHub Marketplace and share your actions with the GitHub community.

Your repository must only include the metadata file, code, and files necessary for the action. 
Creating a single repository for...]]></description><link>https://blog.svarun.dev/how-to-publish-a-github-action</link><guid isPermaLink="true">https://blog.svarun.dev/how-to-publish-a-github-action</guid><category><![CDATA[GitHub]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[2Articles1Week]]></category><category><![CDATA[DevLife]]></category><category><![CDATA[automation]]></category><dc:creator><![CDATA[Varun Sridharan]]></dc:creator><pubDate>Sun, 22 Nov 2020 10:03:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1606039106601/Hz2L-dtK_.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You can publish the actions you have created to the GitHub Marketplace and share your actions with the GitHub community.</p>
<blockquote>
<p>Your repository must only include the metadata file, code, and files necessary for the action. </p>
<p>Creating a single repository for the action allows you tag, release, and package the code in a single unit. </p>
<p>GitHub also uses the action's metadata on your GitHub Marketplace page.</p>
</blockquote>
<h2 id="prerequisites">Prerequisites</h2>
<p>Actions are published to GitHub Marketplace immediately without review, as long as they meet the below requirements:</p>
<ol>
<li>The action must be in a public repository. </li>
<li>Each repository must contain a single action. </li>
<li>The action's metadata file (<code>action.yml</code> or <code>action.yaml</code>) must be in the root directory of the repository. </li>
<li>The <code>name</code> in the action's metadata file must be unique. <ol>
<li>The <code>name</code> cannot match an existing action name on GitHub Marketplace. </li>
<li>The <code>name</code> cannot match a user or organization on GitHub, unless the user or organization owner is publishing the action.</li>
<li>The <code>name</code> cannot match an existing GitHub Marketplace category. </li>
<li>GitHub reserves the names of GitHub features.</li>
</ol>
</li>
</ol>
<h2 id="publishing-and-removing-an-action">Publishing and removing an action</h2>
<p>You can add the action you created to the GitHub Marketplace by tagging it as a new release and publishing it, or you can remove it. Github’s documentation for publishing and removing an action is illustrative and straightforward. You can see the steps in the <a target="_blank" href="https://docs.github.com/en/free-pro-team@latest/actions/creating-actions/publishing-actions-in-github-marketplace#publishing-an-action">Publishing an action</a> section.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://docs.github.com/en/free-pro-team@latest/actions/creating-actions/publishing-actions-in-github-marketplace">https://docs.github.com/en/free-pro-team@latest/actions/creating-actions/publishing-actions-in-github-marketplace</a></div>
<hr />
<div class="hn-embed-widget" id="blog-footer"></div>]]></content:encoded></item><item><title><![CDATA[How To Create A Github Action In Docker]]></title><description><![CDATA[In the previous blog, we have learnt how to create Github actions using Node.js. In this blog we will learn how to build a Github action using Docker. This will be similar to the simple action in Node.js, which will greet the user and set an output v...]]></description><link>https://blog.svarun.dev/how-to-create-a-github-action-in-docker</link><guid isPermaLink="true">https://blog.svarun.dev/how-to-create-a-github-action-in-docker</guid><category><![CDATA[GitHub]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[Docker]]></category><category><![CDATA[DevLife]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Varun Sridharan]]></dc:creator><pubDate>Sat, 21 Nov 2020 11:44:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1605959031974/bqCVC1R-2.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the previous blog, we have learnt how to create Github actions using <code>Node.js</code>. In this blog we will learn how to build a Github action using Docker. This will be similar to the <strong>simple action</strong> in <code>Node.js</code>, which will greet the user and set an output variable containing current timestamp.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ol>
<li>You will need to have a basic understanding of Docker to start with, along with GitHub Actions environment variables and the Docker container filesystem. For better understanding, you can see the sections on  <a target="_blank" href="https://docs.github.com/en/free-pro-team@latest/actions/automating-your-workflow-with-github-actions/using-environment-variables">Using environment variables</a> and <a target="_blank" href="https://docs.github.com/en/free-pro-team@latest/actions/automating-your-workflow-with-github-actions/virtual-environments-for-github-hosted-runners#docker-container-filesystem">Virtual environments for GitHub"</a>.</li>
<li>Create a new repository on Github. For the purposes of this blog, we will be working with a repository titled <code>simple-docker-action</code></li>
<li>Clone the above repository to your local computer</li>
<li>Once cloned, using terminal, change directory to the repository as below:
<code>cd simple-docker-action</code></li>
</ol>
<h2 id="creating-the-actionyml-file">Creating the <code>action.yml</code> file</h2>
<p>Create a new file <code>action.yml</code> in the <code>simple-docker-action</code> directory with the following example code:</p>
<pre><code class="lang-yml"><span class="hljs-attr">name:</span> <span class="hljs-string">'Simple Docker Action'</span>

<span class="hljs-attr">description:</span> <span class="hljs-string">'Basic Docker Action Which uses AlpineOS'</span>

<span class="hljs-attr">inputs:</span>
  <span class="hljs-attr">who-to-greet:</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">'Who to greet'</span>
    <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">default:</span> <span class="hljs-string">'World'</span>

<span class="hljs-attr">outputs:</span>
  <span class="hljs-attr">time:</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">'The time we greeted you'</span>

<span class="hljs-attr">runs:</span>
  <span class="hljs-attr">using:</span> <span class="hljs-string">"docker"</span>
  <span class="hljs-attr">image:</span> <span class="hljs-string">"Dockerfile"</span>
  <span class="hljs-attr">args:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">${{</span> <span class="hljs-string">inputs.who-to-greet</span> <span class="hljs-string">}}</span>
</code></pre>
<p>This metadata defines one <code>who-to-greet</code> input and one <code>time</code> output parameter. To pass inputs to the Docker container, you must declare the input using <code>inputs</code> and pass the input in the <code>args</code> keyword.  </p>
<blockquote>
<p>GitHub will build an image from your <code>Dockerfile</code>, and run commands in a new container using this image.</p>
</blockquote>
<h2 id="creating-entrypointsh">Creating <code>entrypoint.sh</code></h2>
<p>You can choose any base Docker image and, therefore, any language for your action. The following shell script example uses the <code>who-to-greet</code> input variable to <code>print "Hello [who-to-greet]"</code> in the log file.</p>
<p>Next, the script gets the current time and sets it as an output variable that actions running later in a job can use. In order for GitHub to recognize output variables, you must use a workflow command in a specific syntax: <code>echo "::set-output name=&lt;output name&gt;::&lt;value&gt;"</code>. For more information, see <a target="_blank" href="https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-an-output-parameter">Workflow commands for GitHub Actions.</a></p>
<p>The entry point file mentioned in the Dockerfile under the <strong>ENTRYPOINT</strong> must be created in the repository. In this example, we have taken the entry point file as <code>entrypoint.sh</code>. Hence we will create a file called <code>entrypoint.sh</code> in the repository, and add the following code:</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/sh</span>

<span class="hljs-comment"># $1 will provide the value of who-to-greet</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"Hello <span class="hljs-variable">$1</span>"</span>

time=$(date)

<span class="hljs-built_in">echo</span> <span class="hljs-string">"::set-output name=time::<span class="hljs-variable">$time</span>"</span>
</code></pre>
<p>If <code>entrypoint.sh</code> executes without any errors, the action's status is set to success. You can also explicitly set exit codes in your action's code to provide an action's status. For more information, see <a target="_blank" href="https://docs.github.com/en/free-pro-team@latest/actions/creating-actions/setting-exit-codes-for-actions">Setting exit codes for actions</a></p>
<h2 id="creating-dockerfile">Creating <code>Dockerfile</code></h2>
<p>In the repository, create a file called <code>Dockerfile</code></p>
<blockquote>
<p>Dockerfile should be named exactly as <code>Dockerfile</code> with capital D, and no file extension.</p>
</blockquote>
<h3 id="from">FROM</h3>
<p>The first instruction in the <code>Dockerfile</code> must be <strong><em>FROM</em></strong>, which selects a Docker base image. see the <a target="_blank" href="https://docs.docker.com/engine/reference/builder/#from">FROM reference</a> in the Docker documentation</p>
<p>for the sake of this blog we will be using <code>Alpine Linux</code> as base image.</p>
<pre><code><span class="hljs-keyword">FROM</span> alpine:latest
</code></pre><h3 id="copy">COPY</h3>
<p>Next step is to copy <code>entrypoint.sh</code> before we can execute it 
The COPY instruction copies new files or directories from <code>&lt;src&gt;</code> and adds them to the filesystem of the container at the path <code>&lt;dest&gt;</code>.</p>
<blockquote>
<p>Copies <code>entrypoint.sh</code> file from your action repository to the filesystem path <code>/</code> of the container</p>
</blockquote>
<pre><code><span class="hljs-keyword">COPY</span> entrypoint.sh /entrypoint.sh
</code></pre><h3 id="run">RUN</h3>
<p>The <code>RUN</code> instruction will execute any commands in a new layer on top of the current image and commit the results. The resulting committed image will be used for the next step in the <code>Dockerfile</code>.</p>
<blockquote>
<p>We need make <code>entrypoint.sh</code> executable since we copied the file to the runner.</p>
</blockquote>
<pre><code>RUN <span class="hljs-keyword">chmod</span> +<span class="hljs-keyword">x</span> /entrypoint.sh
</code></pre><h3 id="entrypoint">ENTRYPOINT</h3>
<p>An <code>ENTRYPOINT</code> allows you to configure a container that will run as an executable.</p>
<blockquote>
<p>Code file to execute when the docker container starts up (<code>entrypoint.sh</code>)</p>
</blockquote>
<pre><code><span class="hljs-selector-tag">ENTRYPOINT</span> <span class="hljs-selector-attr">[<span class="hljs-string">"/entrypoint.sh"</span>]</span>
</code></pre><p>The final <code>Dockerfile</code> would look like below</p>
<pre><code class="lang-docker">FROM alpine:latest
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
</code></pre>
<h2 id="testing-out-the-action-in-a-workflow">Testing out the action in a workflow</h2>
<p>Before testing the code, make sure to commit the <code>action.yml</code>, <code>entrypoint.sh</code> and <code>Dockerfile</code> files and push to Github.</p>
<p>The workflow file for this action would look as below:</p>
<pre><code class="lang-yml"><span class="hljs-attr">name:</span> <span class="hljs-string">"Docker Simple"</span>

<span class="hljs-attr">on:</span> [<span class="hljs-string">push</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">docker_simple:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">"A Simple Docker Action"</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">"Greet &amp; Output Time"</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">hello</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">learn-with-varunsridharan/github-action/docker-action@main</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">who-to-greet:</span> <span class="hljs-string">'Mona the Octocat'</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">"Get the output time"</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">"The time was $<span class="hljs-template-variable">{{ steps.hello.outputs.time }}</span>"</span>
</code></pre>
<h2 id="output">Output</h2>
<p>When you run the above action with the exact code you should get output something like below</p>
<p><img src="https://s2.do-spaces.com/2020/Nov/21/160595661851.jpg" alt="Output" /></p>
<hr />
<p>You can download / clone the source code from below Github Repository</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/learn-with-varunsridharan/github-action">https://github.com/learn-with-varunsridharan/github-action</a></div>
<p>We will learn about <em><strong>Publishing Github Actions To Github Marketplace</strong></em> in the next blog </p>
<p>Do stay tuned.</p>
<hr />
<div class="hn-embed-widget" id="blog-footer"></div>]]></content:encoded></item><item><title><![CDATA[Creating Github Action Using NodeJS With Github API]]></title><description><![CDATA[In the previous blog titled How To Create A GitHub Action In NodeJS we understood on who to create Github Actions using NodeJS.
In this post we will learn how to create github action which interacts with Github API using actions/core AND actions/gith...]]></description><link>https://blog.svarun.dev/creating-github-action-using-nodejs-with-github-api</link><guid isPermaLink="true">https://blog.svarun.dev/creating-github-action-using-nodejs-with-github-api</guid><category><![CDATA[GitHub]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[DevLife]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Varun Sridharan]]></dc:creator><pubDate>Fri, 20 Nov 2020 11:10:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1605868646865/1LzZ4cPoi.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the previous blog titled <a target="_blank" href="https://blog.svarun.dev/how-to-create-a-github-action-in-nodejs"><strong>How To Create A GitHub Action In NodeJS</strong></a> we understood on who to create Github Actions using NodeJS.</p>
<p>In this post we will learn how to create github action which interacts with Github API using <a target="_blank" href="https://github.com/actions/toolkit/tree/main/packages/core">actions/core</a> AND <a target="_blank" href="https://github.com/actions/toolkit/tree/main/packages/github">actions/github</a> npm packages</p>
<blockquote>
<p>If you haven't yet read the previous blog post. please stop here. first read it and come back.</p>
</blockquote>
<h2 id="prerequisites">Prerequisites</h2>
<ol>
<li><a target="_blank" href="https://nodejs.org/en/download/current/">Download</a>  and install Node.js 12.x or newer, which includes npm</li>
<li>Create a new repository on GitHub. For the purposes of this blog, we will be working with a repository titled <code>githubapi-nodejs-action</code></li>
<li>Clone the above repository to your local computer</li>
<li>Once cloned, using terminal, change directory to the repository as below:
<code>cd githubapi-nodejs-action</code></li>
<li>From your terminal, initialize the directory with a <code>package.json</code> file using:
<code>npm init -y</code></li>
<li><code>@actions/core</code> NPM Package</li>
</ol>
<h2 id="creating-the-action-metadata-file">Creating the Action Metadata file</h2>
<p>Create a new file <strong><code>action.yml</code></strong> in the <code>githubapi-nodejs-action</code> directory with the following example code: </p>
<pre><code class="lang-yml"><span class="hljs-attr">name:</span> <span class="hljs-string">'NodeJS Action With Github API'</span>

<span class="hljs-attr">description:</span> <span class="hljs-string">'use of the `@actions/core` &amp; `@actions/github` package, which is advanced action demo'</span>

<span class="hljs-attr">runs:</span>
  <span class="hljs-attr">using:</span> <span class="hljs-string">'node12'</span>
  <span class="hljs-attr">main:</span> <span class="hljs-string">'dist/index.js'</span>
</code></pre>
<h2 id="writing-actions-javascript-code">Writing Action's Javascript Code</h2>
<p>Since we are interacting with the Github REST API, we first need to import the <code>@actions/github</code> library into the core variable, since the client for the REST API is included in the Octokit</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> github = <span class="hljs-built_in">require</span>( <span class="hljs-string">'@actions/github'</span> );
</code></pre>
<p>Since we are interacting with the API, we need an authentication token, which should be provided by the user in the workflow YML file. The token will be parsed to the REST API client, and the instance will be stored in a variable</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span>( <span class="hljs-keyword">typeof</span> process.env.GITHUB_TOKEN === <span class="hljs-string">'undefined'</span> ) {    
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>( <span class="hljs-string">'GITHUB_TOKEN ENV Variable Is Required'</span> ); 
}
<span class="hljs-keyword">const</span> octokit = github.getOctokit( process.env.GITHUB_TOKEN );
</code></pre>
<p>Information such as event name, payload and other repository information will be available through <code>github.context</code>, which we will save in the <code>context</code> variable</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> context = github.context;
</code></pre>
<p>Now comes the actual action which <strong>creates a new issue</strong> in the current repository. Using the repository information in the <code>context</code> variable, we will add the issue to the repository.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">await</span> octokit.issues.create( {
    ...context.repo,
    <span class="hljs-attr">title</span>: <span class="hljs-string">`New issue! | Action Runner ID #<span class="hljs-subst">${process.env.GITHUB_RUN_NUMBER}</span>`</span>,
    <span class="hljs-attr">body</span>: <span class="hljs-string">`Hello There,
    Current Time Is : <span class="hljs-subst">${time}</span>
    `</span>
} );
</code></pre>
<p>The complete code would look as below:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> core   = <span class="hljs-built_in">require</span>( <span class="hljs-string">'@actions/core'</span> );
<span class="hljs-keyword">const</span> github = <span class="hljs-built_in">require</span>( <span class="hljs-string">'@actions/github'</span> );

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">if</span>( <span class="hljs-keyword">typeof</span> process.env.GITHUB_TOKEN === <span class="hljs-string">'undefined'</span> ) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>( <span class="hljs-string">'GITHUB_TOKEN ENV Variable Is Required'</span> );
        }

        <span class="hljs-keyword">const</span> octokit = github.getOctokit( process.env.GITHUB_TOKEN );
        <span class="hljs-keyword">const</span> context = github.context;
        <span class="hljs-keyword">const</span> time    = ( <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>() ).toTimeString();

<span class="hljs-keyword">await</span> octokit.issues.create( {
    ...context.repo,
    <span class="hljs-attr">title</span>: <span class="hljs-string">`New issue! | Action Runner ID #<span class="hljs-subst">${process.env.GITHUB_RUN_NUMBER}</span>`</span>,
    <span class="hljs-attr">body</span>: <span class="hljs-string">`Hello There,
    Current Time Is : <span class="hljs-subst">${time}</span>
    `</span>
} );

    } <span class="hljs-keyword">catch</span>( error ) {
        core.setFailed( error.message );
    }
}

run();
</code></pre>
<p><em>Please note that Github does not check or install any dependencies. The action code you create must be full packaged containing all dependencies within.</em></p>
<blockquote>
<p>To package your Github action, please install the <a target="_blank" href="https://github.com/vercel/ncc">vercel/ncc</a> NPM package, which is a simple CLI for compiling a Node.js module into a single file, together with all its dependencies, gcc-style.</p>
<p>After installing the <a target="_blank" href="https://github.com/vercel/ncc">vercel/ncc</a> package, you must include a <code>prepare</code> statement in your package.json, which ensures that the compiler runs automatically each time you update the dependencies. Make sure to run <code>npm run prepare</code> before pushing to the repository each time.</p>
</blockquote>
<pre><code class="lang-json"><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"prepare"</span>: <span class="hljs-string">"ncc build ./src/index.js -o dist --source-map --license licenses.txt"</span>
},
</code></pre>
<p>The workflow file for this action, would look like the below:</p>
<pre><code class="lang-yml"><span class="hljs-attr">name:</span> <span class="hljs-string">"NodeJS Action With Github API"</span>

<span class="hljs-attr">on:</span> [<span class="hljs-string">push</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">nodejs_action_with_github_api:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">"A Advanced Node JS Action"</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">"Create Issue With Current Time"</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">learn-with-varunsridharan/github-action/githubapi-nodejs-action@main</span>
        <span class="hljs-attr">env:</span>
          <span class="hljs-attr">GITHUB_TOKEN:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.GITHUB_TOKEN</span> <span class="hljs-string">}}</span>
</code></pre>
<h2 id="output">Output</h2>
<p>When you run the above action with the exact code you should get output something like below</p>
<h3 id="workflow-output">Workflow Output</h3>
<p><img src="https://s2.do-spaces.com/2020/Nov/20/160586903061.jpg" alt="Output" /></p>
<h3 id="example-issue">Example Issue</h3>
<p><img src="https://s2.do-spaces.com/2020/Nov/20/160586907169.jpg" alt="Example" /></p>
<hr />
<p>You can download / clone the source code from below Github Repository</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/learn-with-varunsridharan/github-action">https://github.com/learn-with-varunsridharan/github-action</a></div>
<hr />
<div class="hn-embed-widget" id="blog-footer"></div>]]></content:encoded></item><item><title><![CDATA[How To Create A GitHub Action In NodeJS]]></title><description><![CDATA[This guide uses the GitHub Actions Toolkit Nodejs module to speed up development. For more information, see the actions/toolkit repository.

To ensure your JavaScript actions are compatible with all GitHub-hosted runners ( Ubuntu, Windows, & macOS ),...]]></description><link>https://blog.svarun.dev/how-to-create-a-github-action-in-nodejs</link><guid isPermaLink="true">https://blog.svarun.dev/how-to-create-a-github-action-in-nodejs</guid><category><![CDATA[GitHub]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[DevLife]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Varun Sridharan]]></dc:creator><pubDate>Thu, 19 Nov 2020 12:37:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1605786708556/n9V0wNfk_.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This guide uses the GitHub Actions Toolkit Nodejs module to speed up development. For more information, see the <a target="_blank" href="https://github.com/actions/toolkit">actions/toolkit</a> repository.</p>
<blockquote>
<p>To ensure your JavaScript actions are compatible with all GitHub-hosted runners ( <code>Ubuntu</code>, <code>Windows</code>, &amp; <code>macOS</code> ), the packaged JavaScript code should be in pure JavaScript and not rely on other binaries. 
If it does rely on binaries, you should pack them alongside the code.</p>
</blockquote>
<h2 id="toolkit-helper-packages">Toolkit Helper Packages</h2>
<p>GitHub provides a ready-available library of toolkit packages for Node.js which can be used to build the JavaScript actions. We will look at some of them briefly as below:</p>
<h4 id="actionscorehttpsgithubcomactionstoolkittreemainpackagescore">✔️ <a target="_blank" href="https://github.com/actions/toolkit/tree/main/packages/core">@actions/core</a></h4>
<p>This package provides a set of functions for handling inputs, outputs, results, logging, secrets and variables. Run <code>npm install @actions/core</code> to install this package.</p>
<h4 id="actionsexechttpsgithubcomactionstoolkittreemainpackagesexec">🏃 <a target="_blank" href="https://github.com/actions/toolkit/tree/main/packages/exec">@actions/exec</a></h4>
<p>This package provides a function to execute commands in shell. Run <code>npm install @actions/exec</code> to install this package.</p>
<h4 id="actionsglobhttpsgithubcomactionstoolkittreemainpackagesglob">🍨 <a target="_blank" href="https://github.com/actions/toolkit/tree/main/packages/glob">@actions/glob</a></h4>
<p>This package provides a function to search for files matching glob patterns. Run <code>npm install @actions/glob</code> to install this package.</p>
<h4 id="actionsiohttpsgithubcomactionstoolkittreemainpackagesio">✏️ <a target="_blank" href="https://github.com/actions/toolkit/tree/main/packages/io">@actions/io</a></h4>
<p>This package provides a function to handle disk i/o functions like cp, mv, rmRF, find etc.. Run <code>npm install @actions/io</code> to install this package.</p>
<h4 id="actionsgithubhttpsgithubcomactionstoolkittreemainpackagesgithub"><a target="_blank" href="https://github.com/actions/toolkit/tree/main/packages/github">@actions/github</a></h4>
<p>This package provides a set of functions to interact with Github’s REST API. Run <code>npm install @actions/github</code> to install this package.</p>
<p><em>Please note that Github does not check or install any dependencies. The action code you create must be full packaged containing all dependencies within.</em></p>
<blockquote>
<p>To package your Github action, please install the <a target="_blank" href="https://github.com/vercel/ncc">vercel/ncc</a> NPM package, which is a simple CLI for compiling a Node.js module into a single file, together with all its dependencies, gcc-style.</p>
<p>After installing the <a target="_blank" href="https://github.com/vercel/ncc">vercel/ncc</a> package, you must include a <code>prepare</code> statement in your package.json, which ensures that the compiler runs automatically each time you update the dependencies. Make sure to run <code>npm run prepare</code> before pushing to the repository each time.</p>
</blockquote>
<pre><code class="lang-json"><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"prepare"</span>: <span class="hljs-string">"ncc build ./src/index.js -o dist --source-map --license licenses.txt"</span>
},
</code></pre>
<h2 id="prerequisites">Prerequisites</h2>
<ol>
<li><a target="_blank" href="https://nodejs.org/en/download/current/">Download</a>  and install Node.js 12.x or newer, which includes npm</li>
<li>Create a new repository on GitHub. For the purposes of this blog, we will be working with a repository titled <code>simple-nodejs-action</code></li>
<li>Clone the above repository to your local computer</li>
<li>Once cloned, using terminal, change directory to the repository as below:
<code>cd simple-nodejs-action</code></li>
<li>From your terminal, initialize the directory with a <code>package.json</code> file using:
<code>npm init -y</code></li>
<li><code>@actions/core</code> NPM Package</li>
</ol>
<hr />
<h1 id="simple-github-action">Simple Github Action</h1>
<p>We will be creating a Github action which will greet user and sets a output variable containing current timestamp.</p>
<h2 id="creating-the-action-metadata-file">Creating the Action Metadata file</h2>
<p>Create a new file <strong><code>action.yml</code></strong> in the <code>simple-nodejs-action</code> directory with the following example code: </p>
<pre><code class="lang-yml"><span class="hljs-attr">name:</span> <span class="hljs-string">'NodeJS Simple Action'</span>

<span class="hljs-attr">description:</span> <span class="hljs-string">'use of the `@actions/core` package, which is a simple action'</span>

<span class="hljs-attr">inputs:</span>
  <span class="hljs-attr">who-to-greet:</span>  <span class="hljs-comment"># id of input</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">'Who to greet'</span>
    <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">default:</span> <span class="hljs-string">'World'</span>

<span class="hljs-attr">outputs:</span>
  <span class="hljs-attr">time:</span> <span class="hljs-comment"># id of output</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">'The time we greeted you'</span>

<span class="hljs-attr">runs:</span>
  <span class="hljs-attr">using:</span> <span class="hljs-string">'node12'</span>
  <span class="hljs-attr">main:</span> <span class="hljs-string">'dist/index.js'</span>
</code></pre>
<p>Let us analyze the contents of the <code>action.yml</code> file, for better understanding:</p>
<p>The <code>runs</code> sections tells the runner that the action runs <code>using</code> <strong>node12</strong>, and that the the <code>main</code> <strong>index.js</strong> file is the one that contains the actual code for the action.</p>
<p>The <code>inputs</code> are the parameters passed to the action as input, and the <code>required</code> tells the runner that inputs are required. The <code>output</code> contains the <code>description</code> of the output parameter.</p>
<h2 id="writing-actions-javascript-code">Writing Action's Javascript Code</h2>
<p>The first step in writing a simple action would be to install &amp; import the <code>@actions/core</code> NPM library.</p>
<p>Run <code>npm install @action/core</code> to install and import it in the code like below</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> core = <span class="hljs-built_in">require</span>( <span class="hljs-string">'@actions/core'</span> );
</code></pre>
<p>Next, you need to get the input parameters from the user. In this example, we will consider a parameter named <code>who-to-greet</code>. You would do this as below:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> nameToGreet = core.getInput( <span class="hljs-string">'who-to-greet'</span> );
</code></pre>
<p>Next, print the input parameters, in our case <code>nameToGreet</code>, to the Github action runner as below:</p>
<pre><code class="lang-javascript">core.info( <span class="hljs-string">`Hello <span class="hljs-subst">${nameToGreet}</span>!`</span> );
</code></pre>
<p>Next, set the output parameters. In this case, we are taking the current time as the output. This is done as below:</p>
<pre><code class="lang-javascript">core.setOutput( <span class="hljs-string">"time"</span>, time );
</code></pre>
<p>The final code would be as shown below:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Imports @actions/core npm library into core variable</span>
<span class="hljs-keyword">const</span> core = <span class="hljs-built_in">require</span>( <span class="hljs-string">'@actions/core'</span> );

<span class="hljs-keyword">try</span> {
    <span class="hljs-comment">// Get The Input Value of `who-to-greet`</span>
    <span class="hljs-keyword">const</span> nameToGreet = core.getInput( <span class="hljs-string">'who-to-greet'</span> );

    <span class="hljs-comment">// Prints The Value of nameToGreet in Github Action</span>
    core.info( <span class="hljs-string">`Hello <span class="hljs-subst">${nameToGreet}</span>!`</span> );

    <span class="hljs-comment">// Fetchs The Current Time</span>
    <span class="hljs-keyword">const</span> time = ( <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>() ).toTimeString();

    <span class="hljs-comment">// Sets The Time As Output</span>
    core.setOutput( <span class="hljs-string">"time"</span>, time );
} <span class="hljs-keyword">catch</span>( error ) {
    core.setFailed( error.message );
}
</code></pre>
<p>As soon as you finish the code, you must build the package and push to Github. <strong>Make sure to follow the steps mentioned at the end of the <em>Toolkit Helper Packages</em> section.</strong></p>
<p>The workflow file for this action, would look like the below:</p>
<pre><code class="lang-yml"><span class="hljs-attr">name:</span> <span class="hljs-string">"NodeJS Simple"</span>

<span class="hljs-attr">on:</span> [<span class="hljs-string">push</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">nodejs_simple:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">"A Simple Node JS Action"</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">"Greet &amp; Output Time"</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">hello</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">learn-with-varunsridharan/github-action/nodejs-simple@main</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">who-to-greet:</span> <span class="hljs-string">'Mona the Octocat'</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">"Get the output time"</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">"The time was $<span class="hljs-template-variable">{{ steps.hello.outputs.time }}</span>"</span>
</code></pre>
<h2 id="output">Output</h2>
<p>When you run the above action with the exact code you should get output something like below</p>
<p><img src="https://s2.do-spaces.com/2020/Nov/19/1605786108-159.jpg" alt="Output Image" /></p>
<p>You can download / clone the source code from below Github Repository</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/learn-with-varunsridharan/github-action">https://github.com/learn-with-varunsridharan/github-action</a></div>
<hr />
<p>We will learn about <em><strong>Creating Github Action With Github REST-API Using Javascript</strong></em> in the next blog </p>
<p>Do stay tuned.</p>
<hr />
<div class="hn-embed-widget" id="blog-footer"></div>]]></content:encoded></item><item><title><![CDATA[Creating and working with action.yml]]></title><description><![CDATA[GitHub actions, irrespective of the chosen environment, require a metadata file, which contains information such as title, description, author, etc., and also defines the inputs, outputs and entry-point for the action. GitHub uses this file to unders...]]></description><link>https://blog.svarun.dev/creating-and-working-with-actionyml</link><guid isPermaLink="true">https://blog.svarun.dev/creating-and-working-with-actionyml</guid><category><![CDATA[GitHub]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[DevLife]]></category><category><![CDATA[automation]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Varun Sridharan]]></dc:creator><pubDate>Wed, 18 Nov 2020 11:25:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1605698146904/BLZrCYKoe.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>GitHub actions, irrespective of the chosen environment, require a metadata file, which contains information such as title, description, author, etc., and also defines the inputs, outputs and entry-point for the action. GitHub uses this file to understand that the information contained identifies an action.</p>
<p>Action metadata files use the <strong><code>YAML</code></strong> syntax, identified by a <strong><code>.yml</code></strong> or <strong><code>.yaml</code></strong> extension. The action metadata file is named <strong><code>action.yml</code></strong>. If you're new to YAML, you can <a target="_blank" href="https://www.codeproject.com/Articles/1214409/Learn-YAML-in-five-minutes">read more here</a>.</p>
<h2 id="action-metadata">Action Metadata</h2>
<p><img src="https://s2.do-spaces.com/2020/Nov/18/1605698511-182.png" alt="Meta Info" /></p>
<p>The <strong><code>action.yml</code></strong> relies on seven metas, some of which are required, and others optional, as shown below:</p>
<table>
<thead>
<tr>
<td>Meta</td><td>Description</td></tr>
</thead>
<tbody>
<tr>
<td><code>name</code> *</td><td>The name of your action. GitHub displays the name in the <strong>actions workflow run log</strong> and in the <strong>actions marketplace</strong>.</td></tr>
<tr>
<td><code>author</code></td><td>The name of the action's author.</td></tr>
<tr>
<td><code>description</code></td><td>A short description of the action.</td></tr>
<tr>
<td><code>inputs</code></td><td>Input parameters allow you to specify data that the action expects during runtime. These are stored as environment variables. It is recommended to use lowercase input ids (Uppercase is automatically converted to lowercase).</td></tr>
<tr>
<td><code>outputs</code></td><td>Output parameters allow you to declare data that an action produces. For actions that run in succession, the output data of one action can in turn be used as input for the next action.</td></tr>
<tr>
<td><code>runs</code> *</td><td>Configures the path to the action's code and the application used to execute the code.</td></tr>
<tr>
<td><code>branding</code> *</td><td>You can use a color and Feather icon to create a badge to personalize and distinguish your action. Badges are shown next to your action name in the GitHub Marketplace.</td></tr>
</tbody>
</table>
<p><em>* Required Field</em></p>
<p>The <strong>inputs</strong>, <strong>outputs</strong>, <strong>runs</strong> and <strong>branding</strong> meta contains multiple parameters each, and hence requires an exploration in-depth, which we will learn below.</p>
<hr />
<h2 id="inputs">inputs</h2>
<p><img src="https://s2.do-spaces.com/2020/Nov/18/1605695888-195.jpg" alt="Inputs Args" /></p>
<p>Input parameters allow you to specify data that the action expects to use during runtime. These are stored as environment variables. It is recommended to use lowercase input ids (Uppercase variables are automatically converted to lowercase).</p>
<p>There are four <strong>input parameters</strong> that an action permits, as shown below:</p>
<table>
<thead>
<tr>
<td>Meta</td><td>Type</td><td>Description</td></tr>
</thead>
<tbody>
<tr>
<td><code>inputs.&lt;id&gt;</code> *</td><td><em>string</em></td><td>Unique identifier within the inputs object. The value of  is a map of the input's metadata. It must start with a letter or <code>_</code> and contain only alphanumeric characters, <code>-</code>, or <code>_</code>.</td></tr>
<tr>
<td><code>inputs.&lt;id&gt;.description</code> *</td><td><em>string</em></td><td>Description of the input parameter.</td></tr>
<tr>
<td><code>inputs.&lt;id&gt;.required</code> *</td><td><em>boolean</em></td><td>Indicates if the action requires the input parameter. <code>true</code> if the parameter is required.</td></tr>
<tr>
<td><code>inputs.&lt;id&gt;.default</code></td><td><em>string</em></td><td>Represents a default value. It is used when an input parameter isn't specified in a workflow file.</td></tr>
</tbody>
</table>
<p><em>* Required Field</em></p>
<hr />
<h2 id="outputs">outputs</h2>
<p><img src="https://s2.do-spaces.com/2020/Nov/18/1605696070-163.jpg" alt="Output Args" /></p>
<p>Output parameters allow you to declare data that an action produces as output. For actions that run in succession, the output data of one action can in turn be used as input for the next action.</p>
<p>There are two <strong>output parameters</strong> that an action permits, as shown below:</p>
<table>
<thead>
<tr>
<td>Meta</td><td>Type</td><td>Description</td></tr>
</thead>
<tbody>
<tr>
<td><code>outputs.&lt;id&gt;</code> *</td><td><em>string</em></td><td>Unique identifier within the <code>outputs</code> object. The value of <code>&lt;id&gt;</code> is a map of the outputs’s metadata. It must start with a letter or <code>_</code> and contain only alphanumeric characters, <code>-</code>, or <code>_</code>.</td></tr>
<tr>
<td><code>outputs.&lt;id&gt;.description</code> *</td><td><em>string</em></td><td>Description of the output parameter.</td></tr>
</tbody>
</table>
<p><em>* Required Field</em></p>
<hr />
<h2 id="runs">runs</h2>
<p>Configures the path to the action's code and the application used to execute the code. The <code>runs</code> meta contains a separate set of parameters which is environment-specific. These are discussed environment-wise as below:</p>
<h3 id="javascript-action">JavaScript Action</h3>
<p><img src="https://s2.do-spaces.com/2020/Nov/18/1605696523-18.jpg" alt="JSArgs" /></p>
<p>The <code>runs</code> meta for a JavaScript action, contains six parameters as detailed below:</p>
<table>
<thead>
<tr>
<td>Meta</td><td>Description</td></tr>
</thead>
<tbody>
<tr>
<td><code>using</code> *</td><td>The application used to execute the code specified in <code>main</code>.</td></tr>
<tr>
<td><code>main</code> *</td><td>The file containing the action code.</td></tr>
<tr>
<td><code>pre</code></td><td>Run a script at the start of a job before <code>main:</code> begins, such as a prerequisite setup script. Always runs by default, but can be overridden using <code>pre-if</code></td></tr>
<tr>
<td><code>pre-if</code></td><td>Define conditions for <code>pre:</code>, which will only run if the conditions in <code>pre-if</code> are met. If not set, then <code>pre-if</code> defaults to always().</td></tr>
<tr>
<td><code>post</code></td><td>Run a script at the end of a job after <code>main:</code> has completed. For example, you can use <code>post:</code> to terminate certain processes or remove unneeded files.</td></tr>
<tr>
<td><code>post-if</code></td><td>Define conditions for <code>post:</code> execution, which will only run if the conditions in <code>post-if</code> are met. If not set, then <code>post-if</code> defaults to always().</td></tr>
</tbody>
</table>
<p><em>* Required Field</em></p>
<h3 id="docker-action">Docker Action</h3>
<p><img src="https://s2.do-spaces.com/2020/Nov/18/1605696794-130.jpg" alt="DockerArgs" /></p>
<p>The <code>runs</code> meta for a Docker actions, contains seven parameters as detailed below:</p>
<table>
<thead>
<tr>
<td>Meta</td><td>Description</td></tr>
</thead>
<tbody>
<tr>
<td><code>using</code> *</td><td>You must set this value to 'docker'.</td></tr>
<tr>
<td><code>pre-entrypoint</code></td><td>Run a script before the entrypoint action begins. For example, you can use <code>pre-entrypoint:</code> to run a prerequisite setup script. Always runs by default but can be overridden using <code>pre-if</code>.</td></tr>
<tr>
<td><code>pre-if</code></td><td>Define conditions for <code>pre-entrypoint:</code>, which will only run if the conditions in <code>pre-if</code> are met. If not set, then <code>pre-if</code> defaults to always().</td></tr>
<tr>
<td><code>image</code> *</td><td>The Docker image to use as the container to run the action. Can be Docker base image name, a local Dockerfile, or public image in Docker Hub or another registry. To reference local Dockerfile, use a relative path to your action metadata file.</td></tr>
<tr>
<td><code>env</code></td><td>Key/value map of environment variables to set in the container environment.</td></tr>
<tr>
<td><code>entrypoint</code></td><td>Used when Dockerfile does not specify ENTRYPOINT or you want to override the ENTRYPOINT</td></tr>
<tr>
<td><code>post-entrypoint</code></td><td>Run scripts after <code>entrypoint</code> action is complete. For example, cleanup scripts. This runs by default but can be overridden using <code>post-if</code>.</td></tr>
<tr>
<td><code>post-if</code></td><td>Define conditions for <code>post:</code> execution, which will only run if the conditions in <code>post-if</code> are met. If not set, then <code>post-if</code> defaults to always().</td></tr>
<tr>
<td><code>args</code></td><td>Array of strings that define the inputs for a Docker container, which can include hardcoded strings. GitHub passes the args to the container's ENTRYPOINT when the container starts up.</td></tr>
</tbody>
</table>
<p><em>* Required Field</em></p>
<hr />
<h2 id="branding">branding</h2>
<p><img src="https://s2.do-spaces.com/2020/Nov/18/1605696970-113.jpg" alt="Branding" /></p>
<p>The <code>branding</code> meta contains two parameters, both of which are required if you are publishing to a marketplace, otherwise are optional. They are as below:</p>
<table>
<thead>
<tr>
<td>Meta</td><td>Description</td></tr>
</thead>
<tbody>
<tr>
<td><code>color</code></td><td>Background color of the badge. Can be: <code>white</code>, <code>yellow</code>, <code>blue</code>, <code>green</code>, <code>orange</code>, <code>red</code>, <code>purple</code>, or <code>gray-dark</code>.</td></tr>
<tr>
<td><code>icon</code></td><td>The name of the icon to use. Refer <a target="_blank" href="https://feathericons.com/">Feather Icons</a> for names.</td></tr>
</tbody>
</table>
<p><em>* Required Field</em></p>
<blockquote>
<p><a target="_blank" href="https://twitter.com/__haya14busa__">haya14busa</a> has created a <strong><em>GitHub Actions Branding Cheat sheet</em></strong> which you can use to find icon &amp; color for your GitHub action </p>
</blockquote>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/haya14busa/github-action-brandings">https://github.com/haya14busa/github-action-brandings</a></div>
<hr />
<p>We will learn about <em><strong>Creating Github Actions Using Javascript</strong></em> in the next blog </p>
<p>Do stay tuned.</p>
<hr />
<div class="hn-embed-widget" id="blog-footer"></div>]]></content:encoded></item><item><title><![CDATA[The why, how and creation of GitHub actions]]></title><description><![CDATA[Why use Github actions?
Much of software development involves repetitive tasks, which consumes a major chunk of time. This time can be more efficiently used by the developers in actually producing code, instead of on these repetitive tasks, which can...]]></description><link>https://blog.svarun.dev/the-why-how-and-creation-of-github-actions</link><guid isPermaLink="true">https://blog.svarun.dev/the-why-how-and-creation-of-github-actions</guid><category><![CDATA[GitHub]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[automation]]></category><category><![CDATA[DevLife]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Varun Sridharan]]></dc:creator><pubDate>Tue, 17 Nov 2020 10:53:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1605607970631/N02x7qKpH.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="why-use-github-actions">Why use Github actions?</h2>
<p>Much of software development involves repetitive tasks, which consumes a major chunk of time. This time can be more efficiently used by the developers in actually producing code, instead of on these repetitive tasks, which can easily be automated by GitHub actions.</p>
<p>Further, repetitive tasks tend to become mundane, and can easily cause a step or two to be missed when routinely performing the same tasks. Github actions help automate these tasks and ensure that every step is run precisely each time.</p>
<p>When multiple developers or development teams are working on a large project, CI/CD becomes the core requirement, which allows developers to commit code without affecting the work of others in the team. GitHub actions with its built-in CI/CD fit the bill perfectly.</p>
<hr />
<h2 id="how-github-actions-can-be-useful">How Github actions can be useful?</h2>
<p>The possibilities for GitHub actions are endless. You can make use of useful actions already created by the community, or create customized actions that suit your requirements as needed by your repository.</p>
<p>Generally, Github actions can be useful for four primary groups of tasks:</p>
<h3 id="1-code-scanning">1. Code Scanning</h3>
<p><img src="https://github.blog/wp-content/uploads/2020/09/94614428-fd9a6b80-025a-11eb-85c5-f71a22a3cdee-1.png" alt="Code Scanning" /></p>
<p>Each time a file is committed to the repository, the workflow can be used to trigger an action to scan the code for ‘malicious’ or ‘erroneous’ content. If such content is detected, the developer can be notified via email. Further, the action can also be used to check for sensitive content accidentally left in the file during development, such as passwords or API tokens.</p>
<p>This helps in consistently keeping the code clean, the developer safe from accidentally publishing sensitive content, as well as the users safe from downloading and using malicious or erroneous code.</p>
<h3 id="2-coding-standards">2. Coding Standards</h3>
<p><img src="https://github.blog/wp-content/uploads/2019/03/editor-tools-social-1.png?w=1200" alt="Others" /></p>
<p>An action can be used to scan the committed file and ensure that all the relevant coding standards have been followed by the developer. Accordingly, the developer can be notified via email regarding notices, warnings, and breaches in the standards, for necessary rectification.</p>
<p>This helps make sure the content committed is always up to coding standards and can easily be followed by fellow developers, ensuring ease of maintenance and ongoing development.</p>
<h3 id="3-building-publishing-and-deploying-code">3. Building, Publishing, and Deploying Code</h3>
<p><img src="https://github.blog/wp-content/uploads/2020/04/eanderson-devops-culture.jpeg?w=1200" alt="Build,Publish" /></p>
<p>Most software packages begin as source files written by developers in human-readable code and are required to be compiled into a machine-readable installation package. Github action can be used to automatically build the code from source files as needed, which would then be suitable for publishing or deployment. </p>
<p>Once the code is built, the action can go further and either publish the package (eg: WordPress Plugins) or deploy the same to a live server. This automation eliminates a plethora of possible errors that can be committed by the developer at each stage.</p>
<h3 id="4-miscellaneous">4. Miscellaneous</h3>
<p><img src="https://s2.do-spaces.com/2020/Nov/17/1605608821-174.jpg" alt="Others" /></p>
<p>Github actions can also be used to perform routine tasks which commonly plague software developers such as sending email to users when a new version is launched, auto-updating the changelog, etc.</p>
<p>The creation of actions in Github is only limited by the extent of your customization and the requirement of your repository. Actions can be integrated with GitHub's APIs and any publicly available third-party API.</p>
<hr />
<h2 id="ways-to-create-github-actions">Ways to create Github actions</h2>
<p>Github actions can be created in three environments, depending on the Operating system:</p>
<table>
<thead>
<tr>
<td>Environment</td><td>Linux</td><td>Windows</td><td>MacOS</td></tr>
</thead>
<tbody>
<tr>
<td>Docker container</td><td>✔️</td><td>❌</td><td>❌</td></tr>
<tr>
<td>JavaScript</td><td>✔️</td><td>✔️</td><td>✔️</td></tr>
<tr>
<td>Composite run steps</td><td>✔️</td><td>✔️</td><td>✔️</td></tr>
</tbody>
</table>
<h3 id="docker-container">Docker Container</h3>
<p><img src="https://s2.do-spaces.com/2020/Nov/17/1605609856-144.jpg" alt="Docker Container" /></p>
<p>Actions using the Docker container can only execute on the Linux OS. You must use a Linux OS and have Docker installed to run Docker container actions. The Docker container allows you to use specific versions of an OS, dependencies, tools, and code. It is more consistent and reliable because the consumer of the action need not worry about the tools or dependencies.  </p>
<p>Do keep in mind though, that because of the latency to build and retrieve the container, Docker container actions are slower than JavaScript actions.  </p>
<h3 id="javascript-actions">JavaScript actions</h3>
<p><img src="https://s2.do-spaces.com/2020/Nov/17/1605609910-135.jpg" alt="JS" /></p>
<p>This simplifies the action code and executes faster than a Docker container action. To ensure compatibility with GitHub-hosted runners (Ubuntu, Windows, and macOS), JS actions must be written in pure JavaScript and not rely on other binaries. If you're developing a Node.js project, the <a target="_blank" href="https://github.com/actions/toolkit">GitHub Actions Toolkit</a> provides packages that you can use in your project to speed up development.</p>
<h3 id="composite-run-steps-actions">Composite run steps actions</h3>
<p><img src="https://s2.do-spaces.com/2020/Nov/17/1605610241-148.jpg" alt="Composite" /></p>
<p>This allows you to combine multiple workflow run steps within one action. For example, you can use this feature to bundle together multiple run commands into an action, and then have a workflow that executes the bundled commands a single step using that action. To see an example, check out the <a target="_blank" href="https://docs.github.com/en/free-pro-team@latest/actions/creating-actions/creating-a-composite-run-steps-action">documentation</a>.</p>
<p>I will discuss the steps for creating actions in the Docker container and Javascript environments in this blog series.</p>
<hr />
<p>We will learn about <em>action.yml</em>  metadata file,</p>
<p>in the next blog titled <strong><em>Creating and working with action.yml</em></strong></p>
<p>Do stay tuned.</p>
<hr />
<div class="hn-embed-widget" id="blog-footer"></div>]]></content:encoded></item><item><title><![CDATA[What is Github Action?]]></title><description><![CDATA[The Github Action is a process that allows you to automate your software development workflow by making use of the built-in CI/CD mechanism. CI/CD (Continuous Integration, Continuous Deployment, and Delivery) is a process that reduces the repetitive ...]]></description><link>https://blog.svarun.dev/what-is-github-action</link><guid isPermaLink="true">https://blog.svarun.dev/what-is-github-action</guid><category><![CDATA[GitHub]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[2Articles1Week]]></category><category><![CDATA[automation]]></category><category><![CDATA[DevLife]]></category><dc:creator><![CDATA[Varun Sridharan]]></dc:creator><pubDate>Mon, 16 Nov 2020 11:07:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1605524472097/C_MFLL4aU.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>The Github Action is a process that allows you to automate your software development workflow by making use of the built-in CI/CD mechanism</strong>. CI/CD (Continuous Integration, Continuous Deployment, and Delivery) is a process that reduces the repetitive process of testing and deploying software. It can be used to trigger automated tasks that help to build, test, and deploy your code. </p>
<p>Using Github Actions, you can use events that happen within repositories to trigger a workflow file to perform specified actions.</p>
<p>A simple illustration would work something like this. Let us say you have a repository containing a WordPress Plugin you have built, and would like to trigger certain actions (eg: post a comment to the issue with predefined FAQs and troubleshooting tips) when an issue is opened, deleted or each time an issue has a new comment. </p>
<h2 id="understanding-the-terms-event-workflow-and-action">Understanding the terms <code>Event</code>, <code>Workflow</code> and <code>Action</code></h2>
<p>What do the above terms mean, and which are they in our illustration?</p>
<h3 id="event"><code>EVENT</code></h3>
<p>‘Issue opened’, ‘issue deleted’, and ‘issue comment’ are called events. They trigger the action. </p>
<h3 id="action"><code>ACTION</code></h3>
<p>An ‘action’ is the end task that runs when an ‘event’ happens. An action can be something existing that is created by the community or custom-created by you for your repository’s specific requirements. In our example ‘post comment with FAQs and troubleshooting’ is one action. Another could be ‘send an email notification to yourself’.</p>
<h3 id="workflow"><code>WORKFLOW</code></h3>
<p>The ‘workflow’ is the YML file located inside the workflows folder which actually links the ‘event’ to the ‘action’. The workflow contains the event (eg: issue opened) and specifies what action should run (eg: post comment with FAQs and troubleshooting).</p>
<h3 id="setting-up-the-environment">Setting up the Environment</h3>
<p>These are the steps you would follow, to set up the workflow environment for a repository:</p>
<ol>
<li>Creating the folder structure inside your repository.</li>
<li>Creating a workflow file to listen for an event happening (eg: new issue)</li>
<li>Specifying the actual action (eg: post FAQ comment) that powers the workflow</li>
</ol>
<p>Since this blog article is specifically about Github's actions, I will skip going into the details of items 1 and 2 above, and focus on the actual action itself.</p>
<p>If you would like to know about steps 1 and 2, you can learn them from  <a target="_blank" href="https://blog.gvasquez.dev/how-to-setup-github-actions-on-your-github-repository">How to setup GitHub Actions on your Github repository.</a> by <a class="user-mention" href="https://hashnode.com/@gv14982">Graham Vasquez</a></p>
<hr />
<p>We will learn about specifying the actual action, which is the focus of this blog series, in the next blog titled <strong><em>The why, how and creation of Github actions.</em></strong></p>
<p><em>Do stay tuned.</em></p>
<hr />
<div class="hn-embed-widget" id="blog-footer"></div>]]></content:encoded></item><item><title><![CDATA[Github Markdown Style Guide]]></title><description><![CDATA[In the previous post in this series, we learned how to write markdown in Github and its exclusive features. 
In this post, we are going to learn how we can style our README.md
Text Align
Using align="left" attribute text can be aligned easily and it ...]]></description><link>https://blog.svarun.dev/github-markdown-style-guide</link><guid isPermaLink="true">https://blog.svarun.dev/github-markdown-style-guide</guid><category><![CDATA[GitHub]]></category><category><![CDATA[2Articles1Week]]></category><category><![CDATA[DevLife]]></category><category><![CDATA[markdown]]></category><dc:creator><![CDATA[Varun Sridharan]]></dc:creator><pubDate>Sun, 15 Nov 2020 10:00:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1605429879118/twXNFDnKZ.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the <a target="_blank" href="https://blog.svarun.dev/github-markdown-syntax-guide">previous post</a> in this series, we learned how to write markdown in Github and its exclusive features. </p>
<p>In this post, we are going to learn how we can style our <code>README.md</code></p>
<h2 id="text-alignhttpsgithubcomvarunsridharanblog-demosblobmain202011github20markdown20style20guidetext-alignmd"><a target="_blank" href="https://github.com/varunsridharan/blog-demos/blob/main/2020/11/Github%20Markdown%20Style%20Guide/text-align.md">Text Align</a></h2>
<p>Using <code>align="left"</code> attribute text can be aligned easily and it works with all the tags that markdown supports.</p>
<pre><code class="lang-markdown"><span class="hljs-section">## Text Align - <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span></span></span>
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">align</span>=<span class="hljs-string">"center"</span>&gt;</span></span>This is center aligned!<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">align</span>=<span class="hljs-string">"left"</span>&gt;</span></span>Left aligned<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">align</span>=<span class="hljs-string">"right"</span>&gt;</span></span>Right aligned<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>


<span class="hljs-section">## Text Align - <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span></span></span>
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">align</span>=<span class="hljs-string">"center"</span>&gt;</span></span>This is center aligned!<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">align</span>=<span class="hljs-string">"left"</span>&gt;</span></span>Left aligned<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">align</span>=<span class="hljs-string">"right"</span>&gt;</span></span>Right aligned<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<h4 id="github-output">Github Output</h4>
<blockquote>
<p><img src="https://s2.do-spaces.com/2020/Nov/15/1605431390-1100.jpg" alt="Text Align" /></p>
</blockquote>
<hr />
<h2 id="image-alignhttpsgithubcomvarunsridharanblog-demosblobmain202011github20markdown20style20guideimage-alignmd"><a target="_blank" href="https://github.com/varunsridharan/blog-demos/blob/main/2020/11/Github%20Markdown%20Style%20Guide/image-align.md">Image Align</a></h2>
<pre><code class="lang-markdown"><span class="hljs-section">## Left Align</span>
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> &gt;</span></span><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">align</span>=<span class="hljs-string">"left"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://via.placeholder.com/400x200"</span>&gt;</span></span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span></span><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span></span><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span></span><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span></span>

<span class="hljs-section">## Right Align</span>
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> &gt;</span></span><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">align</span>=<span class="hljs-string">"right"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://via.placeholder.com/400x200"</span>&gt;</span></span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span></span><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span></span><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span></span><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span></span>

<span class="hljs-section">## Center Align</span>
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">align</span>=<span class="hljs-string">"center"</span> &gt;</span></span><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://via.placeholder.com/400x200"</span>&gt;</span></span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>
</code></pre>
<h4 id="github-output">Github Output</h4>
<blockquote>
<p><img src="https://s2.do-spaces.com/2020/Nov/15/1605432179-180.jpg" alt="Image Align" /></p>
</blockquote>
<hr />
<h2 id="list-with-indentationhttpsgithubcomvarunsridharanblog-demosblobmain202011github20markdown20style20guidelist-with-indentationmd"><a target="_blank" href="https://github.com/varunsridharan/blog-demos/blob/main/2020/11/Github%20Markdown%20Style%20Guide/list-with-indentation.md">List With indentation</a></h2>
<pre><code class="lang-html"># List With indentation

<span class="hljs-tag">&lt;<span class="hljs-name">dl</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">dt</span>&gt;</span>List Item 1<span class="hljs-tag">&lt;/<span class="hljs-name">dt</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">dd</span>&gt;</span>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris pharetra, ante vitae gravida rhoncus, nunc nulla posuere felis<span class="hljs-tag">&lt;/<span class="hljs-name">dd</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">dt</span>&gt;</span>List Item 2<span class="hljs-tag">&lt;/<span class="hljs-name">dt</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">dd</span>&gt;</span>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris pharetra, ante vitae gravida rhoncus, nunc nulla posuere felis<span class="hljs-tag">&lt;/<span class="hljs-name">dd</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">dl</span>&gt;</span>


# Nested List With indentation
<span class="hljs-tag">&lt;<span class="hljs-name">dl</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">dt</span>&gt;</span>List Item 1<span class="hljs-tag">&lt;/<span class="hljs-name">dt</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">dd</span>&gt;</span>
    Lorem ipsum dolor sit amet
    <span class="hljs-tag">&lt;<span class="hljs-name">dl</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">dt</span>&gt;</span>Nested List Item 1<span class="hljs-tag">&lt;/<span class="hljs-name">dt</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">dd</span>&gt;</span>
        Lorem ipsum dolor sit amet
        <span class="hljs-tag">&lt;<span class="hljs-name">dl</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">dt</span>&gt;</span>Nested List Item 2<span class="hljs-tag">&lt;/<span class="hljs-name">dt</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">dd</span>&gt;</span>Lorem ipsum dolor sit amet<span class="hljs-tag">&lt;/<span class="hljs-name">dd</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dl</span>&gt;</span>  
      <span class="hljs-tag">&lt;/<span class="hljs-name">dd</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dl</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">dd</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">dl</span>&gt;</span>
</code></pre>
<h4 id="github-output">Github Output</h4>
<blockquote>
<p><img src="https://s2.do-spaces.com/2020/Nov/15/1605432607-186.jpg" alt="List With Indentation" /></p>
</blockquote>
<hr />
<h2 id="dropdownhttpsgithubcomvarunsridharanblog-demosblobmain202011github20markdown20style20guidedropdownmd"><a target="_blank" href="https://github.com/varunsridharan/blog-demos/blob/main/2020/11/Github%20Markdown%20Style%20Guide/dropdown.md">Dropdown</a></h2>
<p>To Make Markdown work inside <code>details</code> tag make sure to leave the empty line after the summary tag</p>
<blockquote>
<p><strong>Dropdown</strong> in markdown supports all the basic features such as Text, Image, Table, List, .etc inside. i am just show some of it. not all.</p>
</blockquote>
<h3 id="text">Text</h3>
<pre><code class="lang-markdown"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">details</span>&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">summary</span>&gt;</span></span>Dropdown With Text<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">summary</span>&gt;</span></span>
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris pharetra, ante vitae gravida rhoncus, nunc nulla posuere felis, 
  id rhoncus ante tellus malesuada libero. Aliquam fermentum massa eget sodales aliquet.
  Morbi vitae velit tincidunt lorem fringilla lacinia non vitae leo. Fusce congue odio eget tellus finibus vulputate. 
<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">details</span>&gt;</span></span>
</code></pre>
<h4 id="github-output">Github Output</h4>
<blockquote>
<p><img src="https://s2.do-spaces.com/2020/Nov/15/1605433339-129.gif" alt="Text Dropdown" /></p>
</blockquote>
<h3 id="image">Image</h3>
<pre><code class="lang-markdown"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">details</span>&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">summary</span>&gt;</span></span>Dropdown With Image<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">summary</span>&gt;</span></span>

  ![<span class="hljs-string">Image Here</span>](<span class="hljs-link">https://via.placeholder.com/600x200</span>)  
<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">details</span>&gt;</span></span>
</code></pre>
<h4 id="github-output">Github Output</h4>
<blockquote>
<p><img src="https://s2.do-spaces.com/2020/Nov/15/1605433474-117.gif" alt="Image" /></p>
</blockquote>
<h3 id="table">Table</h3>
<pre><code class="lang-markdown"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">details</span>&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">summary</span>&gt;</span></span>Dropdown With Table<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">summary</span>&gt;</span></span>

  | First Header | Second Header |
  | --- | --- |
  | Content from cell 1 | Content from cell 2 |
  | Content in the first column | Content in the second column |

<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">details</span>&gt;</span></span>
</code></pre>
<h4 id="github-output">Github Output</h4>
<blockquote>
<p><img src="https://s2.do-spaces.com/2020/Nov/15/1605433559-135.gif" alt="Table" /></p>
</blockquote>
<hr />
<p>You can check out the live demo @ this repository</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/varunsridharan/blog-demos/tree/main/2020/11/Github%20Markdown%20Style%20Guide">https://github.com/varunsridharan/blog-demos/tree/main/2020/11/Github%20Markdown%20Style%20Guide</a></div>
<hr />
<div class="hn-embed-widget" id="blog-footer"></div>]]></content:encoded></item></channel></rss>