Jekyll2022-01-08T00:27:12+00:00https://sahinakkaya.dev/feed.xmlŞahin Akkaya’s Personal PageŞahin Akkaya's personal blog - a perfectionist who likes to tinker everything until it is just right. Get ready to find some sweet tips that will boost your productivity and make you fall in love with your computer.Şahin AkkayaAutomatically Build and Deploy Your Site using GitHub Actions and Webhooks2022-01-04T17:40:00+00:002022-01-04T17:40:00+00:00https://sahinakkaya.dev/2022/01/04/build-and-deploy-automatically<p>In this post I will explain how you can use GitHub to automate the build and deployment processes that you have. I am going to automate the deployment of this site but you can do whatever you want. Just understanding the basics will be enough.</p>
<h2 id="introduction-to-github-actions-and-webhooks">Introduction to GitHub Actions and Webhooks</h2>
<p>Let me start by explaining what are GitHub Actions and GitHub Webhooks.</p>
<blockquote>
<p><strong>Github Actions</strong> is a continuous integration and continuous delivery (CI/CD) platform that allows you to automate your build, test, and deployment pipeline. You can create workflows that build and test every pull request to your repository, or deploy merged pull requests to production.</p>
</blockquote>
<blockquote>
<p><strong>Webhooks</strong> provide a way for notifications to be delivered to an external web server whenever certain actions occur on a repository or organization. … For example, you can configure a webhook to execute whenever:</p>
<ul>
<li>A repository is pushed to</li>
<li>A pull request is opened</li>
<li>A GitHub Pages site is built</li>
<li>A new member is added to a team</li>
</ul>
</blockquote>
<h2 id="defining-the-problem-and-solution">Defining the problem and solution</h2>
<p>As I said, my example will be automating the deployment of this site. Here is the normal workflow of me doing it manually:
<img src="/assets/images/gh-actions-and-webhooks/workflow.png" alt="My workflow" /></p>
<p>As you can see, the only place where my work is really required is writing the post. Other two steps can be automated. We will use GitHub Actions to generate the site content and Webhooks to let our server know about the new content so it can pull the changes. Let’s get started.</p>
<h3 id="setting-up-github-actions">Setting up GitHub Actions</h3>
<p>Setting up a GitHub Action is as easy as creating a <code class="language-plaintext highlighter-rouge">.yml</code> file in <code class="language-plaintext highlighter-rouge">.github/workflows/</code> directory in your repository. Let us create a new action to build our site. Fortunately, there is already a <a href="https://github.com/marketplace/actions/jekyll-actions">GitHub action</a> to do it for us. Create a file called <code class="language-plaintext highlighter-rouge">.github/workflows/jekyll.yml</code> in your root directory of your repository and put the following contents:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Jekyll site CI</span>
<span class="na">on</span><span class="pi">:</span>
<span class="na">push</span><span class="pi">:</span>
<span class="na">branches</span><span class="pi">:</span> <span class="pi">[</span> <span class="nv">main</span> <span class="pi">]</span>
<span class="na">pull_request</span><span class="pi">:</span>
<span class="na">branches</span><span class="pi">:</span> <span class="pi">[</span> <span class="nv">main</span> <span class="pi">]</span>
<span class="na">jobs</span><span class="pi">:</span>
<span class="na">build</span><span class="pi">:</span>
<span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
<span class="na">steps</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v2</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Jekyll Actions</span>
<span class="na">uses</span><span class="pi">:</span> <span class="s">helaili/jekyll-action@2.2.0</span>
<span class="na">with</span><span class="pi">:</span>
<span class="na">token</span><span class="pi">:</span> <span class="s">${{ secrets.GITHUB_TOKEN }}</span>
<span class="na">keep_history</span><span class="pi">:</span> <span class="no">true</span>
<span class="na">target_branch</span><span class="pi">:</span> <span class="s1">'</span><span class="s">gh-pages'</span>
</code></pre></div></div>
<p>That’s it! We have created our first Action. When we push this change, GitHub will start building our site and push the result to <code class="language-plaintext highlighter-rouge">gh-pages</code> branch. Currently, it will take a while to build because we don’t use caching. So let’s include it to build faster. Add the following piece as a second step:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Use GitHub Actions' cache to shorten build times and decrease load on servers</span>
<span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/cache@v2</span>
<span class="na">with</span><span class="pi">:</span>
<span class="na">path</span><span class="pi">:</span> <span class="s">vendor/bundle</span>
<span class="na">key</span><span class="pi">:</span> <span class="s">${{ runner.os }}-gems-${{ hashFiles('**/Gemfile') }}</span>
<span class="na">restore-keys</span><span class="pi">:</span> <span class="pi">|</span>
<span class="s">${{ runner.os }}-gems-</span>
</code></pre></div></div>
<p>We are done with the Actions part. You can see the final code <a href="https://github.com/Asocia/sahinakkayadotdev/blob/main/.github/workflows/jekyll.yml">here</a>. When you are also done with the code, just push it to trigger the action.</p>
<h3 id="setting-up-the-webhook-and-related-endpoint">Setting up the Webhook and related endpoint</h3>
<p>Now that we set up our Action to build the site, we need to let our server know about the changes so that it can pull the changes.</p>
<h4 id="creating-a-webhook-from-github">Creating a Webhook from GitHub</h4>
<p>To add a Webhook, open your repository in browser and navigate to <em>‘Settings > Webhooks’</em> and click <em>‘Add Webhook’</em>. Fill in the form with appropriate values. Here is an example:
<img src="/assets/images/gh-actions-and-webhooks/add-webhook.png" alt="Webhook form example" /></p>
<p>This is all you have to do from GitHub. Now, whenever there is a <em><code class="language-plaintext highlighter-rouge">push</code></em> event to your repository, GitHub will send a POST request to your <em>payload url</em> with the details.</p>
<p class="notice--info"><strong>Note:</strong> Our Action is configured to push to a branch in our repository, so it will also trigger this hook and we will catch it.</p>
<h4 id="creating-an-endpoint-to-handle-the-requests">Creating an endpoint to handle the requests</h4>
<p>I will use <a href="https://flask.palletsprojects.com/en/2.0.x/">Flask</a> framework to handle the post requests coming to our endpoint. You can use whatever programming language or framework you want. It will be very simple code with just one job: Validate the secret keys and run a specific code.</p>
<p>Let’s start by creating a new project and a virtual environment:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mdkir post_receiver
<span class="nb">cd </span>post_receiver
python3 <span class="nt">-m</span> venv venv
<span class="nb">source </span>venv/bin/activate
</code></pre></div></div>
<p>Install the required packages:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip <span class="nb">install </span>Flask gunicorn
</code></pre></div></div>
<p>Create a new file for storing our environment variables:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># config.py
</span>
<span class="n">APP_KEY</span> <span class="o">=</span> <span class="s">"your-secret-key"</span> <span class="c1"># same key that is used in github while creating the webhook
</span><span class="n">PROJECT_PATH</span> <span class="o">=</span> <span class="s">"/path/to/your/project/"</span> <span class="c1"># you will want to cd into this path and perform commands such as git pull etc.
</span></code></pre></div></div>
<p>And create the Flask application:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># post_receiver.py
</span>
<span class="kn">import</span> <span class="nn">hashlib</span>
<span class="kn">import</span> <span class="nn">hmac</span>
<span class="kn">import</span> <span class="nn">subprocess</span>
<span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">request</span>
<span class="kn">import</span> <span class="nn">config</span>
<span class="n">application</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
<span class="o">@</span><span class="n">application</span><span class="p">.</span><span class="n">route</span><span class="p">(</span><span class="s">'/'</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">'GET'</span><span class="p">,</span> <span class="s">'POST'</span><span class="p">])</span>
<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
<span class="k">if</span> <span class="n">request</span><span class="p">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">'GET'</span><span class="p">:</span>
<span class="k">return</span> <span class="s">'OK'</span>
<span class="k">elif</span> <span class="n">request</span><span class="p">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">'POST'</span><span class="p">:</span>
<span class="n">content</span> <span class="o">=</span> <span class="n">request</span><span class="p">.</span><span class="n">data</span>
<span class="n">secret</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">config</span><span class="p">.</span><span class="n">APP_KEY</span><span class="p">,</span> <span class="s">'utf-8'</span><span class="p">)</span>
<span class="n">digester</span> <span class="o">=</span> <span class="n">hmac</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="n">secret</span><span class="p">,</span> <span class="n">content</span><span class="p">,</span> <span class="n">hashlib</span><span class="p">.</span><span class="n">sha256</span><span class="p">)</span>
<span class="n">calculated_signature</span> <span class="o">=</span> <span class="s">'sha256='</span> <span class="o">+</span> <span class="n">digester</span><span class="p">.</span><span class="n">hexdigest</span><span class="p">()</span>
<span class="n">signature</span> <span class="o">=</span> <span class="n">request</span><span class="p">.</span><span class="n">headers</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'X-Hub-Signature-256'</span><span class="p">)</span>
<span class="k">if</span> <span class="n">calculated_signature</span> <span class="o">==</span> <span class="n">signature</span><span class="p">:</span>
<span class="n">subprocess</span><span class="p">.</span><span class="n">Popen</span><span class="p">(</span>
<span class="p">[</span><span class="s">'./perform-git-pull.sh'</span><span class="p">,</span> <span class="n">config</span><span class="p">.</span><span class="n">PROJECT_PATH</span><span class="p">])</span>
<span class="k">return</span> <span class="s">'OK'</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="s">'Error'</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="n">application</span><span class="p">.</span><span class="n">run</span><span class="p">(</span><span class="n">host</span><span class="o">=</span><span class="s">'0.0.0.0'</span><span class="p">)</span>
</code></pre></div></div>
<p>I will not go into details explaining what each line does. Basically, we are checking if the request is a POST request and if so we are comparing the secret keys to make sure that the request is coming from GitHub. In our case, this is not too important because when the keys match we are running simple git commands in our repository but you might need it if you are doing something more complicated. And here is the contents of <code class="language-plaintext highlighter-rouge">perform-git-pull.sh</code> file:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="nb">cd</span> <span class="nv">$1</span>
git checkout gh-pages
git pull
</code></pre></div></div>
<p>We are almost done! All we need to do is create a service to automatically run our code and let nginx handle our endpoint correctly.</p>
<p>Create a new file <code class="language-plaintext highlighter-rouge">post_receiver.service</code> in <code class="language-plaintext highlighter-rouge">/etc/systemd/system/</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#/etc/systemd/system/post_receiver.service
# change <user> to your actual username
[Unit]
Description=post_receiver
After=network.target multi-user.target
[Service]
User=<user>
Environment="PYTHONPATH=/home/<user>/post_receiver/venv/bin/python"
WorkingDirectory=/home/<user>/post_receiver
ExecStart=/home/<user>/post_receiver/venv/bin/gunicorn -b 127.0.0.1:5000 -w 2 --log-file /home/<user>/post_receiver/post_receiver.log post_receiver
[Install]
WantedBy=multi-user.target
</code></pre></div></div>
<p>Make sure port <code class="language-plaintext highlighter-rouge">5000</code> is reachable from outside.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>ufw allow 5000
<span class="nb">sudo </span>ufw <span class="nb">enable</span>
</code></pre></div></div>
<p>Finally, edit your nginx configuration, <code class="language-plaintext highlighter-rouge">/etc/nginx/sites-available/yoursite</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>location = /postreceive/ {
proxy_pass http://localhost:5000/;
}
</code></pre></div></div>
<p>Start, restart the services</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>systemctl daemon-reload
<span class="nb">sudo </span>systemctl start post_receiver
<span class="nb">sudo </span>systemctl <span class="nb">enable </span>post_receiver
<span class="nb">sudo </span>systemctl restart nginx
</code></pre></div></div>
<p>That’s it! <code class="language-plaintext highlighter-rouge">curl https://yourdomain.com/postreceive/</code> should return <code class="language-plaintext highlighter-rouge">"OK"</code> and we are ready to accept POST requests from GitHub.</p>
<h3 id="notes-for-debugging">Notes for debugging</h3>
<p>In case anything goes wrong, here are a few tips to debug:</p>
<ul>
<li>Every GitHub Action produces a log that you can examine. Check them to see if anything is odd.</li>
<li>In the <em>Webhooks</em> tab, there is a sub-tab called <em>Recent Deliveries</em>. You can take a look at there to see the results of the requests from your hooks.</li>
<li>You can always test your code locally with <code class="language-plaintext highlighter-rouge">curl</code>:
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>❯ curl -i -X POST -H 'Content-Type: application/json' -d '{"foo": "bar", "bar": "baz"}' https://yourdomain.com/postreceive/
</code></pre></div> </div>
</li>
</ul>
<p>Happy hacking!</p>Şahin AkkayaIn this post I will explain how you can use GitHub to automate the build and deployment processes that you have. I am going to automate the deployment of this site but you can do whatever you want. Just understanding the basics will be enough.Stop cat-pipe’ing, You Are Doing It Wrong!2022-01-01T15:00:00+00:002022-01-01T15:00:00+00:00https://sahinakkaya.dev/2022/01/01/stop-cat-pipeing<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat </span>some_file | <span class="nb">grep </span>some_pattern
</code></pre></div></div>
<p>I’m sure that you run a command something like above at least once if you are using terminal. You know how <code class="language-plaintext highlighter-rouge">cat</code> and <code class="language-plaintext highlighter-rouge">grep</code> works and you also know what pipe (<code class="language-plaintext highlighter-rouge">|</code>) does. So you naturally combine all of these to make the job done. I was also doing it this way. What I didn’t know is that <code class="language-plaintext highlighter-rouge">grep</code> already accepts file as an argument. So the above command could be rewritten as:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">grep </span>some_pattern some_file
</code></pre></div></div>
<p>… which can make you save a few keystrokes and a few nanoseconds of CPU cycles. Phew! Not a big deal if you are not working files that contains GBs of data, right? I agree but you should still use the latter command because it will help you solve some other problems better. Here is a real life scenario: You want to search for some specific pattern in all the files in a directory.</p>
<ul>
<li>If you use the first approach, you may end up running commands like this:</li>
</ul>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>❯ <span class="nb">ls</span>
config.lua Git.lua init.lua markdown.lua palette.lua util.lua
diff.lua highlights.lua LSP.lua Notify.lua Treesitter.lua Whichkey.lua
❯ <span class="nb">cat </span>config.lua | <span class="nb">grep </span>light
❯ <span class="nb">cat </span>diff.lua | <span class="nb">grep </span>light
❯ <span class="nb">cat </span>Git.lua | <span class="nb">grep </span>light
❯ <span class="nb">cat </span>highlights.lua | <span class="nb">grep </span>light
Pmenu <span class="o">=</span> <span class="o">{</span> <span class="nb">fg</span> <span class="o">=</span> C.light_gray, <span class="nb">bg</span> <span class="o">=</span> C.popup_back <span class="o">}</span>,
CursorLineNr <span class="o">=</span> <span class="o">{</span> <span class="nb">fg</span> <span class="o">=</span> C.light_gray, style <span class="o">=</span> <span class="s2">"bold"</span> <span class="o">}</span>,
Search <span class="o">=</span> <span class="o">{</span> <span class="nb">fg</span> <span class="o">=</span> C.light_gray, <span class="nb">bg</span> <span class="o">=</span> C.search_blue <span class="o">}</span>,
IncSearch <span class="o">=</span> <span class="o">{</span> <span class="nb">fg</span> <span class="o">=</span> C.light_gray, <span class="nb">bg</span> <span class="o">=</span> C.search_blue <span class="o">}</span>,
❯ <span class="nb">cat </span>init.lua | <span class="nb">grep </span>light
<span class="nb">local </span>highlights <span class="o">=</span> require <span class="s2">"onedarker.highlights"</span>
highlights,
❯ <span class="c"># You still have a lot to do :/</span>
</code></pre></div></div>
<ul>
<li>If you use the second approach, you will immediately realize that you can send all the files with <code class="language-plaintext highlighter-rouge">*</code> operator and you will finish the job with just one command (2 if you include mandatory <code class="language-plaintext highlighter-rouge">ls</code> :D):</li>
</ul>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>❯ <span class="nb">ls</span>
config.lua Git.lua init.lua markdown.lua palette.lua util.lua
diff.lua highlights.lua LSP.lua Notify.lua Treesitter.lua Whichkey.lua
❯ <span class="nb">grep </span>light <span class="k">*</span>
highlights.lua: Pmenu <span class="o">=</span> <span class="o">{</span> <span class="nb">fg</span> <span class="o">=</span> C.light_gray, <span class="nb">bg</span> <span class="o">=</span> C.popup_back <span class="o">}</span>,
highlights.lua: CursorLineNr <span class="o">=</span> <span class="o">{</span> <span class="nb">fg</span> <span class="o">=</span> C.light_gray, style <span class="o">=</span> <span class="s2">"bold"</span> <span class="o">}</span>,
highlights.lua: Search <span class="o">=</span> <span class="o">{</span> <span class="nb">fg</span> <span class="o">=</span> C.light_gray, <span class="nb">bg</span> <span class="o">=</span> C.search_blue <span class="o">}</span>,
highlights.lua: IncSearch <span class="o">=</span> <span class="o">{</span> <span class="nb">fg</span> <span class="o">=</span> C.light_gray, <span class="nb">bg</span> <span class="o">=</span> C.search_blue <span class="o">}</span>,
init.lua:local highlights <span class="o">=</span> require <span class="s2">"onedarker.highlights"</span>
init.lua: highlights,
LSP.lua: NvimTreeNormal <span class="o">=</span> <span class="o">{</span> <span class="nb">fg</span> <span class="o">=</span> C.light_gray, <span class="nb">bg</span> <span class="o">=</span> C.alt_bg <span class="o">}</span>,
LSP.lua: LirFloatNormal <span class="o">=</span> <span class="o">{</span> <span class="nb">fg</span> <span class="o">=</span> C.light_gray, <span class="nb">bg</span> <span class="o">=</span> C.alt_bg <span class="o">}</span>,
markdown.lua: markdownIdDelimiter <span class="o">=</span> <span class="o">{</span> <span class="nb">fg</span> <span class="o">=</span> C.light_gray <span class="o">}</span>,
markdown.lua: markdownLinkDelimiter <span class="o">=</span> <span class="o">{</span> <span class="nb">fg</span> <span class="o">=</span> C.light_gray <span class="o">}</span>,
palette.lua: light_gray <span class="o">=</span> <span class="s2">"#abb2bf"</span>,
palette.lua: light_red <span class="o">=</span> <span class="s2">"#be5046"</span>,
util.lua:local <span class="k">function </span>highlight<span class="o">(</span>group, properties<span class="o">)</span>
util.lua: <span class="s2">"highlight"</span>,
util.lua: highlight<span class="o">(</span>group, properties<span class="o">)</span>
</code></pre></div></div>
<p>Isn’t this neat? You might say that <em>“This is cheating! You are using a wild card, of course it will be easier.”</em> Well, yes. Technically I could use the same wild card in the first command like <code class="language-plaintext highlighter-rouge">cat * | grep light</code> but:</p>
<ul>
<li>I figured that out only after using wild card in the second command. So I think it is does not feel natural.</li>
<li>It is still not giving the same output. Try and see the difference! <a href="##" title="You will not be able to see which file contains which line. 'cat' will just concatenate all the input.">*</a></li>
</ul>Şahin Akkayacat some_file | grep some_pattern I’m sure that you run a command something like above at least once if you are using terminal. You know how cat and grep works and you also know what pipe (|) does. So you naturally combine all of these to make the job done. I was also doing it this way. What I didn’t know is that grep already accepts file as an argument. So the above command could be rewritten as: grep some_pattern some_fileFirst blog post2021-12-24T23:54:08+00:002021-12-24T23:54:08+00:00https://sahinakkaya.dev/2021/12/24/first-blog-post<style>
.ab {
font-size: 1.3em;
line-height:0;
top:0;
}
.cd {
text-decoration: none;
}
</style>
<p><em><code class="language-plaintext highlighter-rouge">Hello, World!</code></em><a href="##" title="I think I just wrote the best first sentence I could write as a programmer :D" class="cd"><sup class="ab">*</sup></a>
So here I am and welcome to my first blog. Having a personal space on the Internet has been a dream for me for years and I am happy that it finally have come true. You might think that I could sign-up for a social media platform and my profile would be a personal space for me but no. I just don’t feel comfortable with that way. This has been the case since my childhood and also the reason why I don’t use Facebook, Instagram or any other social media. If you think you found me on these platforms, I would say it is not me. I might write another post about why I don’t like social media but I will cut this one here.</p>
<h2 id="why-i-wanted-to-start-blogging">Why I wanted to start blogging?</h2>
<p>There are several reasons for starting my own site and blogging, but I can list the most important ones as follows:</p>
<h3 id="giving-back-to-community">Giving back to community</h3>
<p>I use the software developed and brought by the community every day. The moment I power on my computer I start using Free Software. It really amazes me to see the work produced by people who do not know each other at all. For example, I did not even write a single line of code for this site. If Free Software didn’t exist, I’d either have to spend money and use a platform that I have limited control over, or waste my time and build a site with a possibly worse design than this one<a href="##" title="swh" class="cd"><sup class="ab">*</sup></a>. In return for this, I want to give back to the community. For me, the way to give back to the community so far has been to share the projects I’ve done and archive the things I learn every day in a repository called <a href="https://github.com/Asocia/til">TIL</a><a href="##" title="Today I Learned" class="cd"><sup class="ab">*</sup></a>. But some of the til’s I’ve written recently are getting lengthy and I think they deserve their own posts. So instead of writing long til’s, I will blog what I learned here.</p>
<h3 id="archiving-the-memories">Archiving the memories</h3>
<p>I like to go over what I have done in the past once in a while. Blogging is perfect way to do this. I still read my diaries that I wrote in the past and they are fun. But I promise I will keep these posts more formal than my diaries<a href="##" title="swh" class="cd"><sup class="ab">*</sup></a>.</p>
<h3 id="pushing-myself-to-do-something-useful">Pushing myself to do something useful</h3>
<p>At the end of every year, I sit on my desk and think about what I did in that year. I generally don’t like the result because I fail to keep some of my resolutions for that year. Setting up a personal website was one of my resolutions for 2021 and it looks like I manage to keep it<a href="##" title="hooray!" class="cd"><sup class="ab">*</sup></a><a href="##" title="swh" class="cd"><sup class="ab">*</sup></a>. Unfortunately, I can’t always keep my spirits up. Sometimes I just do nothing and all the time passes. Hopefully, the feeling that I have to write something will help me get out of bad mood at such times.</p>
<h3 id="improving-my-writing-skills">Improving my writing skills</h3>
<p>Last but not least, I want to improve my writing. Even though I don’t use a formal language while writing here, I think it will help me improve my writing skills.</p>
<h2 id="final-words">Final words</h2>
<p>While writing this post I already come up with some new topics to write but I think they need their own posts.</p>
<p>Subscribe to my <a href="/feed.xml"><i class="fas fa-fw fa-rss-square" aria-hidden="true"></i>RSS Feed</a> to not miss them. You know RSS, right? I recently started using it and it is the best way to consume content. Do yourself a favor and search it if you don’t know. I will probably write something about it in the following blog posts. That’s all from me and thank you for reading. See you next time!</p>Şahin AkkayaHello, World!* So here I am and welcome to my first blog. Having a personal space on the Internet has been a dream for me for years and I am happy that it fi...