<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Minh Trinh</title>
    <description>The latest articles on DEV Community by Minh Trinh (@anhtm).</description>
    <link>https://dev.to/anhtm</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F75930%2F1dbbc004-cf3c-4292-be45-d6e4be275379.jpg</url>
      <title>DEV Community: Minh Trinh</title>
      <link>https://dev.to/anhtm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/anhtm"/>
    <language>en</language>
    <item>
      <title>How I Reverted My Addiction to YouTube</title>
      <dc:creator>Minh Trinh</dc:creator>
      <pubDate>Thu, 11 Feb 2021 18:04:14 +0000</pubDate>
      <link>https://dev.to/anhtm/how-i-reverted-my-addiction-to-youtube-2nhl</link>
      <guid>https://dev.to/anhtm/how-i-reverted-my-addiction-to-youtube-2nhl</guid>
      <description>&lt;p&gt;I was addicted to YouTube and social media, like, &lt;em&gt;alarmingly&lt;/em&gt; addicted. Talk about wasting 3-4 hours a day just watching endless YouTube videos. I had no control over my life or my time. I knew I had to do something about it.&lt;/p&gt;

&lt;p&gt;A few weeks ago, I picked up &lt;a href="https://www.calnewport.com/books/digital-minimalism/" rel="noopener noreferrer"&gt;Digital Minimalism&lt;/a&gt; by Cal Newport. In the book, he suggested removing all optional technology in 30 days - including any online service, news or apps that do not prevent you from functioning on a day-to-day basis. This process is called "digital decluttering". It aims to give you time to reflect on what matters.&lt;/p&gt;

&lt;p&gt;Following his guide, I decided to cut down YouTube for 30 days.&lt;/p&gt;

&lt;p&gt;Leaving YouTube was difficult. It was my primary source of entertainment. It was where I've learned pretty much anything - from learning a new language, coding to unclogging the sink.&lt;/p&gt;

&lt;p&gt;To my surprise, after a month and a series of steps that I took to discourage myself from using YouTube, I no longer have the urge to use it anymore. It's just not &lt;em&gt;that&lt;/em&gt; interesting to go and browse videos and watch them. I managed to reduce my YouTube time to less than 15 minutes on average per day. That equals to one YouTube video. &lt;/p&gt;

&lt;p&gt;With COVID and quarantine life, I might not be alone in this battle against the infinite loop of excessive streaming habit and inner scream for help. Here is the list of my strategies and I hope you might benefit from it!&lt;/p&gt;

&lt;h3&gt;
  
  
  1.  Find alternative hobbies
&lt;/h3&gt;

&lt;p&gt;This first step is the most important. It is crucial to find other hobbies to not relapse back to YouTube. There are a few criteria for a high-quality hobby:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Analog&lt;/em&gt;: I highly recommend you find a few hobbies that don't need technology to function. This will create distance between you and your phone, which means less opportunity to pick up the phone and watch a random video. &lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Fun&lt;/em&gt;: It should be fun for you. It should spark interest, curiosity, passion - whatever floats your boat. It should be rewarding. I've started to read much more books than I used to. I also enjoy walking outdoor more, especially during COVID when nothing is open.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Requires skills&lt;/em&gt;: Find something that requires skills that you hold valuable - something you need to work on to "level up". They can be physical or mental. For example, learning a new instrument requires both physical and intellectual capability. A few examples: learning to sew, playing a new sport, playing chess, running, writing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you have a hobby that you need to invest your time and energy in, you tend to stick to it more to reap the rewards. Also, if you want to reintroduce YouTube back, you now have a set of topics in mind that YouTube can help open horizon. YouTube will then become your tool and will serve you instead of manipulating your attention.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fuhi7s5k07x378pvlcaef.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fuhi7s5k07x378pvlcaef.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I picked up my long lost hobby - playing the piano! It's great for the brain and soothes my soul.&lt;br&gt;&lt;/p&gt;

&lt;p&gt;My recommendation is to find at least two fun and analog hobbies, and one of the two should require skills.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.  Remove YouTube from your phone
&lt;/h3&gt;

&lt;p&gt;Once you've acquired a few new hobbies, remove YouTube from your phone. This step seems obvious, but I feel the need to include it here. I'm just a normal human with all of the desires and without any tremendous amount of willpower, so I have to create external barriers to not rely on my inner self-control.&lt;/p&gt;

&lt;p&gt;Having YouTube available at your fingertips is dangerous. In a split second of boredom, our brain will activate a subconscious urge to order our hand to open the app. This also gives YouTube permission to send notifications by default. That would equal constant distraction and allow YouTube to sneak in and steal our time without us even realizing it. Of course, you can disable the notifications from the settings, but in my experience, not having YouTube at all on the phone allows me to &lt;em&gt;forget&lt;/em&gt; about its existence altogether. &lt;/p&gt;

&lt;p&gt;If you use an Android device, it's impossible to uninstall the app (uhh Google, please fix this 🤦‍♀️). The most you can do is disable YouTube, and in doing so, YouTube will no longer show up in your applications list. You'll probably receive a warning when trying to disable it, but don't worry, nothing will go out of order if you do so. Google will try its best to keep us hooked, and remember, it's always easy to enable it back on.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.  Opt out of activity tracking
&lt;/h3&gt;

&lt;p&gt;In other words, &lt;em&gt;make YouTube&lt;/em&gt; &lt;em&gt;dumb&lt;/em&gt;. I stumbled upon this feature while tinkering with my privacy settings on Google, and it honestly changed the game!&lt;/p&gt;

&lt;p&gt;By opting out of YouTube activity tracking, Google will stop saving your watch history &amp;amp; search history in the future. There's also an option to delete my past data so I went ahead and deleted all my history.&lt;/p&gt;

&lt;p&gt;After this change, I noticed YouTube still proposes videos that would interest me, but the recommendation engine would show more videos that I've already watched. As a result, YouTube became a bit duller and less engaging than it used to be.&lt;/p&gt;

&lt;p&gt;To do so, all you need to do is go to &lt;a href="https://myactivity.google.com/" rel="noopener noreferrer"&gt;https://myactivity.google.com/&lt;/a&gt; &amp;gt; &lt;em&gt;YouTube History&lt;/em&gt; and toggle off the YouTube History option: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fajgynbu0jyp6svfik5ul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fajgynbu0jyp6svfik5ul.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's an extra step if you want to delete your data. Just go to the &lt;em&gt;Manage activity&lt;/em&gt; tab and choose which date range you want to delete your data from.&lt;/p&gt;

&lt;p&gt;With this simple switch off, it takes me &lt;em&gt;less&lt;/em&gt; effort and willpower to get over YouTube.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Block YouTube from your browser
&lt;/h3&gt;

&lt;p&gt;I use &lt;a href="https://blocksite.co/" rel="noopener noreferrer"&gt;BlockSite&lt;/a&gt;, which essentially is just a tool that blocks your access to certain websites of your choice. When a site is blocked, you don't have instant access to it by default and have to type in your password to access the site. There are also options to add even more friction such as adding extra challenges to unblock the page. Remember, make it inconvenient.&lt;/p&gt;

&lt;p&gt;This is what I see when I type &lt;code&gt;youtube.com&lt;/code&gt; in my browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4wik6ahzwn2efa1sqqz4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4wik6ahzwn2efa1sqqz4.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I intentionally set my password to something ridiculous and difficult to remember and, funnily enough, I forgot my password 😜. Thanks to my laziness, I didn't bother resetting the password and stopped visiting YouTube altogether.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Schedule your watch time
&lt;/h3&gt;

&lt;p&gt;Another effective strategy is to plan your watch time. Ask yourself a few questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When am I going to watch YouTube?&lt;/li&gt;
&lt;li&gt;For how long am I going to use it?&lt;/li&gt;
&lt;li&gt;What am I going to watch?&lt;/li&gt;
&lt;li&gt;Do I watch it alone or with somebody else?&lt;/li&gt;
&lt;li&gt;Why do I want to watch YouTube?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, I set out my schedule as follows: watch YouTube for a maximum of 30 minutes a day, after dinner at around 9 PM. Watch 1 or 2 chess game analysis videos from @agadmator because I want to follow what's going on in the current chess tournament.&lt;/p&gt;

&lt;p&gt;By setting myself up to a specific plan, I'm less likely to go down the YouTube rabbit hole. I don't feel guilty for watching YouTube anymore because it now has become a tool. I regain control of my time and only use YouTube to follow my fostered hobbies.&lt;/p&gt;

&lt;p&gt;Next, keep this schedule in a spot that's easy to see so that you have a constant reminder of your intention. For example, write it on a piece of paper and stick it on the wall. Or put a sticky note on your laptop.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Track your progress, but don't beat yourself up
&lt;/h3&gt;

&lt;p&gt;YouTube has a handy built-in digital wellbeing tool called "Time watched profile" for tracking daily YouTube watch time. It also keeps track of your historical data for the last seven days and the rolling average. A good start would be getting into the habit of checking your watch time daily and becoming more aware of your time spent on the app.&lt;/p&gt;

&lt;p&gt;Additionally, I keep track of my screen time with the &lt;em&gt;Digital Wellbeing&lt;/em&gt; app on Android (or &lt;em&gt;Screen Time&lt;/em&gt; for iOS users). Every day, I note my screen time on an external app to have a concentrated data source and further recognize my patterns. I use Notion to track progress, but you can use pretty much anything - an Excel table or even a paper calendar works too!&lt;/p&gt;

&lt;p&gt;I don't think it's necessary to set a specific goal, like "spending less than 1 hour on YouTube". The primary purpose of progress tracking is to help you shift your focus to mindfulness. Results will come when we are intentional in our actions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fx34rm3pircbqots9jgv1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fx34rm3pircbqots9jgv1.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I keep track of other wellbeing factors besides screen time, but you don't have to if that's too much for you. The general idea here is to be mindful of your actions.&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Finally, have fun with it! Don't beat yourself up if you don't remember your progress for one day, or if you used YouTube 1 hour more than you should today. Just remind yourself to get back to it tomorrow. Every new day is a fresh start 🎉&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;These are the solutions that I've tried and have worked for me personally. Some of these ideas might work for you while some might not. I'd suggest trying them out one by one and give yourself some time to get familiar with this new "regime". It's going to be difficult for the first week or two. You'll likely feel a strong sense of boredom and an urge to open up YouTube or any other time-sucking apps like Reddit or Twitter. It's completely normal to feel that way. Just remind yourself that you have a handful of hobbies to cultivate that will give you much more joy and satisfaction than spending 2+ hours on YouTube.&lt;/p&gt;

&lt;p&gt;I hope this article proves to be useful in any way to you. Let me know in the comments!&lt;/p&gt;

&lt;p&gt;Good luck 🍀&lt;/p&gt;




&lt;p&gt;Sources&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cover &lt;span&gt;Photo by &lt;a href="https://unsplash.com/@vmxhu?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Szabo Viktor&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/youtube?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/span&gt; &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>productivity</category>
    </item>
    <item>
      <title>GitLab CI: Creating your own pipeline template library 👷</title>
      <dc:creator>Minh Trinh</dc:creator>
      <pubDate>Tue, 19 May 2020 13:00:42 +0000</pubDate>
      <link>https://dev.to/anhtm/a-comprehensive-guide-to-creating-your-own-gitlab-ci-template-library-5b3i</link>
      <guid>https://dev.to/anhtm/a-comprehensive-guide-to-creating-your-own-gitlab-ci-template-library-5b3i</guid>
      <description>&lt;p&gt;As developers, we all know that it is a good idea to reuse code as much as possible. We all know the DRY mantra - &lt;em&gt;Don't Repeat Yourself&lt;/em&gt;. Functions, classes or web components abstract away logic, parameterize data, allow code to be reusable, maintainable and extendable. &lt;/p&gt;

&lt;p&gt;With &lt;a href="https://docs.gitlab.com/ee/ci/"&gt;GitLab CI/CD&lt;/a&gt;, pipelines are defined in &lt;a href="https://yaml.org/"&gt;YAML&lt;/a&gt;. It's a &lt;em&gt;human-readable data- serialization language&lt;/em&gt; (&lt;a href="https://en.wikipedia.org/wiki/YAML"&gt;Source&lt;/a&gt;), so its focus is clear and concise data delivery rather than efficiency optimization. It's often not obvious how we can go about reusing code in YAML.&lt;/p&gt;

&lt;p&gt;Today, we're going to learn how to create  &lt;strong&gt;CI template library&lt;/strong&gt; - a library comprised of reusable job templates that can be shared, extended, and overridden by multiple projects 👌&lt;/p&gt;

&lt;h2&gt;
  
  
  But first, why should I create a CI template library?
&lt;/h2&gt;

&lt;p&gt;If you're a developer working on a side project on GitLab, or if your team is relatively small and your projects are diverse in programming languages and build processes, then it's probably good enough to stick to a single &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;On the other hand, as a company grows, it often has standardized testing, building, and deploying processes that are applied to most internal projects. A CI template library increases &lt;strong&gt;time efficiency&lt;/strong&gt; in pipeline development and decreases &lt;strong&gt;update and maintenance effort&lt;/strong&gt; across the ecosystem.&lt;/p&gt;

&lt;p&gt;To build a template library, let's first dive into the basic component of it - the job template ⬇️&lt;/p&gt;

&lt;h2&gt;
  
  
  Job template
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;job&lt;/code&gt; is a basic building block of a pipeline. It usually has a single purpose, executed in isolation and, most of the time, independent of other jobs. &lt;/p&gt;

&lt;p&gt;For example, let's say we have an awesome Node.js app called &lt;code&gt;awesome-node-app&lt;/code&gt; and we need a job to install dependencies before building it. The &lt;code&gt;install&lt;/code&gt; job would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# awesome-node-app/.gitlab-ci.yml&lt;/span&gt;

&lt;span class="na"&gt;install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;node_modules/&lt;/span&gt; &lt;span class="c1"&gt;# cache node_modules/ in subsequent pipeline&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# install dependencies in CI mode&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, a &lt;strong&gt;job template&lt;/strong&gt; is essentially a job, but it has the following extra properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Generic&lt;/strong&gt;: It is project-agnostic which means it does not contain any data that pertains to a specific project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Importable&lt;/strong&gt;: It is easily imported and used directly in a project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customizable&lt;/strong&gt;: It can be extended or overridden.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we examine the &lt;code&gt;install&lt;/code&gt; job above once again, it seems like we could, and should, transform it into a template. We might want to reuse it in another JS application in the future! &lt;/p&gt;

&lt;p&gt;It's already &lt;em&gt;generic&lt;/em&gt; enough, but not quite &lt;em&gt;importable&lt;/em&gt; or &lt;em&gt;customizable&lt;/em&gt;. Let's change that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making a template
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create and include a template
&lt;/h3&gt;

&lt;p&gt;To create a template, all we need to do is move the job to a new file, &lt;code&gt;install.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# awesome-node-app/install.yml&lt;/span&gt;

&lt;span class="na"&gt;install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we need to figure a way to "import" and use this job in &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; file. Luckily, GitLab has a pretty sweet keyword &lt;a href="https://docs.gitlab.com/ee/ci/yaml/#include"&gt;&lt;code&gt;include&lt;/code&gt;&lt;/a&gt; that allows us to do exactly that!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;code&gt;include&lt;/code&gt; allows us to include and use content declared in an external &lt;code&gt;yml&lt;/code&gt; or &lt;code&gt;yaml&lt;/code&gt; file - either locally or remotely. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We already created &lt;code&gt;install.yml&lt;/code&gt; locally, so let's &lt;code&gt;include&lt;/code&gt; it at the top of our &lt;code&gt;.gitlab-ci.yml&lt;/code&gt;, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# awesome-node-app/.gitlab-ci.yml&lt;/span&gt;

&lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;local&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;install.yml'&lt;/span&gt; &lt;span class="c1"&gt;# path to `install.yml`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pipeline now has the job named &lt;code&gt;install&lt;/code&gt; that does the same thing:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--j1pkkd1e--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/lvzib5va711sv515xr8m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--j1pkkd1e--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/lvzib5va711sv515xr8m.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using local file here probably won't make sense since we might want to reuse the template in another project and we do &lt;em&gt;not&lt;/em&gt; want to just copy and paste the job definition. Remember, keep it DRY. &lt;/p&gt;

&lt;p&gt;So let's go ahead and make a new template library that only stores templates! We can then refer to this library whenever we need to &lt;code&gt;install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create a new repository &lt;code&gt;ci-templates&lt;/code&gt; that's within the same group as &lt;code&gt;awesome-node-app&lt;/code&gt;. Then, add &lt;code&gt;install.yml&lt;/code&gt; at the root of the project. By now, you would have this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ci-templates/
  | install.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, commit and push to  &lt;code&gt;master&lt;/code&gt;. Cool! Now your library is up and accessible to other repositories.&lt;/p&gt;

&lt;p&gt;Let's go back to &lt;code&gt;awesome-node-app/.gitlab-ci.yml&lt;/code&gt;, and &lt;code&gt;include&lt;/code&gt;  &lt;code&gt;install.yml&lt;/code&gt; again, this time using &lt;a href="https://docs.gitlab.com/ee/ci/yaml/#includefile"&gt;&lt;code&gt;include:file&lt;/code&gt;&lt;/a&gt; directive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .gitlab-ci.yml&lt;/span&gt;

&lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="c1"&gt;# group name is your username if the project is under personal account&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;my-group&amp;gt;/ci-templates'&lt;/span&gt;  
    &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;master'&lt;/span&gt;
    &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;install.yml'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡We can modify &lt;code&gt;ref&lt;/code&gt; to point to any other branch, commit SHA, or version tag of the file in Git history as we'd like. It's good practice to keep track of version history for your template file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Great! We just basically told GitLab to "include this file &lt;code&gt;install.yml&lt;/code&gt; from &lt;code&gt;ci-templates&lt;/code&gt; repo on &lt;code&gt;master&lt;/code&gt; branch into the pipeline". We now have &lt;code&gt;install&lt;/code&gt; job imported from &lt;code&gt;ci-templates&lt;/code&gt; to our &lt;code&gt;awesome-node-app&lt;/code&gt; 💪&lt;/p&gt;

&lt;p&gt;We can run the pipeline as-is - &lt;code&gt;install&lt;/code&gt; is activated automatically without any further configuration. &lt;/p&gt;

&lt;p&gt;However, what if we do want to change or add configuration?&lt;/p&gt;

&lt;h3&gt;
  
  
  Customize a template
&lt;/h3&gt;

&lt;p&gt;Scenario: Right now, &lt;code&gt;install&lt;/code&gt; only looks for dependencies declared in the &lt;code&gt;package.json&lt;/code&gt; file at the root of a project. What if we have &lt;code&gt;another-awesome-node-app&lt;/code&gt; that is a monorepo, and we want to run &lt;code&gt;install&lt;/code&gt; multiple times in various locations?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;another-awesome-node-app/
  | project_one/
  |__ package.json
  | project_two/
  |__ package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to parameterize our &lt;code&gt;install&lt;/code&gt; template to take in some sort of data that holds information about the &lt;strong&gt;location&lt;/strong&gt; of the &lt;code&gt;package.json&lt;/code&gt; file we're looking for. &lt;/p&gt;

&lt;p&gt;The most powerful way to parameterize a template is by using &lt;a href="https://docs.gitlab.com/ee/ci/variables/README.html"&gt;&lt;strong&gt;environment variables&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Environment variables come in two flavours:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Predefined environment variables&lt;/strong&gt;: Variables provided by GitLab out of the box and ready to use without any specification. &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡They are references to branch names, merge request IDs, jobs info, and &lt;a href="https://docs.gitlab.com/ee/ci/variables/predefined_variables.html"&gt;much, much more&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Predefined environment variables are incredibly powerful. We can do things like conditionally skipping a job in a pipeline, allowing jobs to run on certain branches, leveraging custom variables, and so on. &lt;/p&gt;

&lt;p&gt;This topic deserves a separate article of its own, so if you're interested in knowing more about their use cases and real-world implementation, let me know in the comments below 💚&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Custom environment variables&lt;/strong&gt;: Variables defined in &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; (you can also define them in GitLab UI and via the API).&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Make sure to avoid name collision with predefined variables when naming your variable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Custom environment variables work in great harmony with job templates. The syntax is as follow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# declare a key/value pair&lt;/span&gt;
    &lt;span class="na"&gt;MY_VARIABLE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello'&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;# declare as many variables as you want &lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# call its value, this outputs "hello" in the runner&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo $MY_VARIABLE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that the variable is declared within the job scope. This means that the variable is only accessible within the job and inaccessible from pipeline level.&lt;/p&gt;

&lt;p&gt;Going back to our example, let's create a new custom &lt;code&gt;variable&lt;/code&gt; named &lt;code&gt;INSTALL_DIRECTORY&lt;/code&gt; and call it in our &lt;code&gt;install&lt;/code&gt; script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ci-templates/install.yml&lt;/span&gt;

&lt;span class="na"&gt;install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;INSTALL_DIRECTORY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.'&lt;/span&gt; &lt;span class="c1"&gt;# default to root directory&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;$INSTALL_DIRECTORY/node_modules/&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# cd to the directory of package.json&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cd $INSTALL_DIRECTORY&lt;/span&gt;
    &lt;span class="c1"&gt;# install dependencies in CI mode&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One more thing before we move on, let's make the job &lt;strong&gt;hidden&lt;/strong&gt; by default by changing the job name from &lt;code&gt;install&lt;/code&gt; to &lt;code&gt;.install&lt;/code&gt;. I'll explain how this works in just a bit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ci-templates/install.yml&lt;/span&gt;

&lt;span class="s"&gt;.install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;...&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cool! Now we're ready to use this template in  &lt;code&gt;another-awesome-node-app&lt;/code&gt;. Let's include the template again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# another-awesome-node-app/.gitlab-ci.yml&lt;/span&gt;

&lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;my-group&amp;gt;/ci-templates'&lt;/span&gt;
    &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;master'&lt;/span&gt;
    &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;install.yml'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've just included &lt;code&gt;.install&lt;/code&gt;, but this time, it's &lt;a href="https://docs.gitlab.com/ee/ci/yaml/#hide-jobs"&gt;&lt;strong&gt;hidden&lt;/strong&gt;&lt;/a&gt;, which means it's disabled by default. If you try running this pipeline in GitLab, it will not run simply because the pipeline is &lt;strong&gt;empty&lt;/strong&gt; - there is no job!&lt;/p&gt;

&lt;p&gt;So how do we use our template then?&lt;/p&gt;

&lt;p&gt;Turns out, we can create a new job that &lt;a href="https://docs.gitlab.com/ee/ci/yaml/#extends"&gt;&lt;strong&gt;&lt;code&gt;extends&lt;/code&gt;&lt;/strong&gt;&lt;/a&gt; our template to inherit its configuration. &lt;/p&gt;

&lt;p&gt;Let's create two jobs &lt;code&gt;install_project_one&lt;/code&gt; and &lt;code&gt;install_project_two&lt;/code&gt; that extend &lt;code&gt;.install&lt;/code&gt;. After that, we also need to change the default value of &lt;code&gt;INSTALL_DIRECTORY&lt;/code&gt; in each job to the expected path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# another-awesome-node-app/.gitlab-ci.yml&lt;/span&gt;

&lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;...&lt;/span&gt;

&lt;span class="na"&gt;install_project_one&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.install&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;INSTALL_DIRECTORY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;project_one/'&lt;/span&gt;

&lt;span class="na"&gt;install_project_two&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.install&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;INSTALL_DIRECTORY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;project_two/'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Awesome! Now both &lt;code&gt;install_project_one&lt;/code&gt; and &lt;code&gt;install_project_two&lt;/code&gt; inherit the script from &lt;code&gt;.install&lt;/code&gt;, but they find the &lt;code&gt;package.json&lt;/code&gt; file in two different locations just like we wanted!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Had we not specified &lt;code&gt;install&lt;/code&gt; a &lt;strong&gt;hidden&lt;/strong&gt; job earlier, we would have had one extra &lt;code&gt;install&lt;/code&gt; job declared in our pipeline that runs in the root directory - where there is no &lt;code&gt;package.json&lt;/code&gt;. This will fail the pipeline.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;📝 To prevent side effects from including external jobs, it's good practice to declare all template jobs &lt;strong&gt;hidden&lt;/strong&gt; and &lt;code&gt;extend&lt;/code&gt; them when needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going beyond templates
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mixins
&lt;/h3&gt;

&lt;p&gt;We can also keep any other reusable snippets of configuration in the template library. I'd like to call them &lt;code&gt;mixins&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Some mixin examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bash scripts&lt;/li&gt;
&lt;li&gt;Pipeline configuration partials&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's one good example of what I meant by &lt;em&gt;pipeline configuration partial&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;.auth_gitlab_registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker:dind&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY&lt;/span&gt;
  &lt;span class="na"&gt;after_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker logout $CI_REGISTRY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This mixin logs the user into GitLab Registry &lt;code&gt;before_script&lt;/code&gt; and logs them out &lt;code&gt;after_script&lt;/code&gt;. However, it does not have &lt;code&gt;script&lt;/code&gt; declared, which means that it cannot be run as a &lt;code&gt;job&lt;/code&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡&lt;code&gt;script&lt;/code&gt; is required for job definition.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To use the mixin, all we need to do is &lt;code&gt;extend&lt;/code&gt; it the same way we do with regular templates and, most importantly, define what's in &lt;code&gt;script&lt;/code&gt; in the new job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;
&lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;...&lt;/span&gt;

&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.auth_gitlab_registry&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker build $MY_APP_IMAGE&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker push $MY_APP_IMAGE&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Here's what we've learned today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Properties of a job template: &lt;strong&gt;generic&lt;/strong&gt;, &lt;strong&gt;importable&lt;/strong&gt; and &lt;strong&gt;customizable&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Include&lt;/code&gt; and &lt;code&gt;extend&lt;/code&gt; a template&lt;/li&gt;
&lt;li&gt;Use environment variables to parameterize templates&lt;/li&gt;
&lt;li&gt;Hide a job to prevent side effects&lt;/li&gt;
&lt;li&gt;Use mixins to further simplify the pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm sure there are many other techniques in building a CI template not listed here. Please let me know in the comments what you think of my approach and any suggestions/recommendations for further optimization!&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this article 💚&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover photo by Pankaj Patel on Unsplash&lt;/em&gt;&lt;/p&gt;

</description>
      <category>gitlab</category>
      <category>devops</category>
      <category>pipeline</category>
      <category>cicd</category>
    </item>
  </channel>
</rss>
