<?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: Tom</title>
    <description>The latest articles on DEV Community by Tom (@__timor__).</description>
    <link>https://dev.to/__timor__</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%2F3467933%2F59c9ba5a-aa93-4aeb-8322-6acd8fa401e5.png</url>
      <title>DEV Community: Tom</title>
      <link>https://dev.to/__timor__</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/__timor__"/>
    <language>en</language>
    <item>
      <title>Transmission: Permission Denied to Downloads directory</title>
      <dc:creator>Tom</dc:creator>
      <pubDate>Fri, 26 Dec 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/__timor__/transmission-permission-denied-to-downloads-directory-2pam</link>
      <guid>https://dev.to/__timor__/transmission-permission-denied-to-downloads-directory-2pam</guid>
      <description>&lt;p&gt;Transmission on Ubuntu may be denied access to localized Downloads directories due to AppArmor profiles expecting English folder names. Here’s how to fix it.&lt;/p&gt;

&lt;p&gt;Check: &lt;a href="https://gagor.pro/2025/12/transmission-permission-denied-to-downloads-directory/" rel="noopener noreferrer"&gt;https://gagor.pro/2025/12/transmission-permission-denied-to-downloads-directory/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Create Markdown presentations with Marp</title>
      <dc:creator>Tom</dc:creator>
      <pubDate>Thu, 06 Nov 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/__timor__/create-markdown-presentations-with-marp-46b3</link>
      <guid>https://dev.to/__timor__/create-markdown-presentations-with-marp-46b3</guid>
      <description>&lt;p&gt;Learn how to create beautiful slide decks with Markdown and Marp, a simple and powerful tool for presentations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gagor.pro/2025/11/create-markdown-presentations-with-marp/" rel="noopener noreferrer"&gt;https://gagor.pro/2025/11/create-markdown-presentations-with-marp/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>markdown</category>
      <category>ai</category>
    </item>
    <item>
      <title>Crosspost Your Blog articles to Social Media</title>
      <dc:creator>Tom</dc:creator>
      <pubDate>Sun, 14 Sep 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/__timor__/crosspost-your-blog-articles-to-social-media-2pnm</link>
      <guid>https://dev.to/__timor__/crosspost-your-blog-articles-to-social-media-2pnm</guid>
      <description>&lt;p&gt;Learn how to automatically share your latest blog posts to Mastodon, Twitter/X, and other social networks using GitHub Actions and the crosspost tool. A simple way to regain traffic lost to search engines and AI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gagor.pro/2025/09/crosspost-your-blog-articles-to-social-media/" rel="noopener noreferrer"&gt;https://gagor.pro/2025/09/crosspost-your-blog-articles-to-social-media/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>seo</category>
      <category>hugo</category>
      <category>github</category>
      <category>wordpress</category>
    </item>
    <item>
      <title>How I stopped worrying and loved Makefiles</title>
      <dc:creator>Tom</dc:creator>
      <pubDate>Fri, 29 Aug 2025 15:53:56 +0000</pubDate>
      <link>https://dev.to/__timor__/how-i-stopped-worrying-and-loved-makefiles-1af7</link>
      <guid>https://dev.to/__timor__/how-i-stopped-worrying-and-loved-makefiles-1af7</guid>
      <description>&lt;h2&gt;
  
  
  First contact with make
&lt;/h2&gt;

&lt;p&gt;When I was invited for my first job interview in the IT, I've been asked such question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How would you typically build a program from sources, what commands will you use?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I answered:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It's obvious:&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./configure
make
make install
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;p&gt;Those times belong to the past now and nowadays not many programmers use GNU Make&lt;sup id="fnref1"&gt;1&lt;/sup&gt;. Try asking this question and you will see disgust at best.&lt;/p&gt;

&lt;p&gt;For many it's the fist contact with &lt;code&gt;make&lt;/code&gt; and often the last one, but not for me 😉&lt;/p&gt;

&lt;h2&gt;
  
  
  What &lt;code&gt;make&lt;/code&gt; does?
&lt;/h2&gt;

&lt;p&gt;Let build a base line. &lt;code&gt;make&lt;/code&gt; orchestrates tasks based on dependencies, executing commands to generate target files and keep them up to date efficiently. It streamlines software compilation and project management.&lt;/p&gt;

&lt;p&gt;As simple as it is, it does few things pretty well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;detects changes in files (source -&amp;gt; binary),&lt;/li&gt;
&lt;li&gt;manages dependencies,&lt;/li&gt;
&lt;li&gt;manages default values for variables much easier than Bash,&lt;/li&gt;
&lt;li&gt;allows to build in parallel,&lt;/li&gt;
&lt;li&gt;OS detection,&lt;/li&gt;
&lt;li&gt;on my system binary is only 16kB in size,&lt;/li&gt;
&lt;li&gt;is available on any OS,&lt;/li&gt;
&lt;li&gt;and much more!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My second contact with make - Postfix
&lt;/h2&gt;

&lt;p&gt;The second non obvious use of &lt;code&gt;make&lt;/code&gt; I stood, was the way to refresh Postfix's map files. With Postfix servers, you've been usually writing a bunch of text files like &lt;code&gt;aliases&lt;/code&gt;, &lt;code&gt;transports&lt;/code&gt;, etc, which have to be indexed to the binary Berkley DB format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alias_maps = hash:/etc/postfix/aliases
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To "generate" Berkley DB file you have to run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;postalias aliases
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which was producing &lt;code&gt;aliases.db&lt;/code&gt; file. Other types of files required to use &lt;code&gt;postmap&lt;/code&gt; command to generate them.&lt;/p&gt;

&lt;p&gt;The more complicated the Postfix configuration, the more files you had. If you missed to update one of map files, your configuration wasn't effective and you could spend hours debugging: why this f... alias do not work?&lt;/p&gt;

&lt;p&gt;That's where &lt;code&gt;make&lt;/code&gt; comes handy &lt;sup id="fnref2"&gt;2&lt;/sup&gt;. You can just drop the file like this:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="nl"&gt;all&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;aliases.db access.db virtual.db reload&lt;/span&gt;

&lt;span class="nl"&gt;aliases.db&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;aliases&lt;/span&gt;
    postalias aliases

&lt;span class="nl"&gt;access.db&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;access&lt;/span&gt;
    postmap access

&lt;span class="nl"&gt;virtual.db&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;virtual&lt;/span&gt;
    postmap virtual

&lt;span class="nl"&gt;reload&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    postfix reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let me explain what happens here.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;.PHONY: reload&lt;/code&gt;: This line declares the target &lt;code&gt;reload&lt;/code&gt; as a phony target &lt;sup id="fnref3"&gt;3&lt;/sup&gt;. Phony targets are ones that do not represent actual files. This is typically used for targets that don't produce output files, such as clean, all, etc. It ensures that even if a file named &lt;code&gt;reload&lt;/code&gt; exists in the directory, the &lt;code&gt;reload&lt;/code&gt; target will still be executed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;all: aliases.db access.db virtual.db reload&lt;/code&gt;: This line specifies that when you run &lt;code&gt;make all&lt;/code&gt;, it will generate the files &lt;code&gt;aliases.db&lt;/code&gt;, &lt;code&gt;access.db&lt;/code&gt;, and &lt;code&gt;virtual.db&lt;/code&gt;, and then execute the &lt;code&gt;reload&lt;/code&gt; target. As &lt;code&gt;all&lt;/code&gt; target is the default, it's enough to just run &lt;code&gt;make&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;aliases.db: aliases&lt;/code&gt;: This line specifies that &lt;code&gt;aliases.db&lt;/code&gt; depends on the file &lt;code&gt;aliases&lt;/code&gt;. If &lt;code&gt;aliases&lt;/code&gt; file is newer than &lt;code&gt;aliases.db&lt;/code&gt; or &lt;code&gt;aliases.db&lt;/code&gt; doesn't exist, the commands listed below will be executed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;postalias aliases&lt;/code&gt;: This line is the command to generate the &lt;code&gt;aliases.db&lt;/code&gt; file from the &lt;code&gt;aliases&lt;/code&gt; file using the &lt;code&gt;postalias&lt;/code&gt; command. &lt;code&gt;postalias&lt;/code&gt; is a command used in Postfix to create or update the alias database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Similarly, &lt;code&gt;access.db: access&lt;/code&gt; and &lt;code&gt;virtual.db: virtual&lt;/code&gt; are rules to generate &lt;code&gt;access.db&lt;/code&gt; and &lt;code&gt;virtual.db&lt;/code&gt; files from &lt;code&gt;access&lt;/code&gt; and &lt;code&gt;virtual&lt;/code&gt; files respectively using the &lt;code&gt;postmap&lt;/code&gt; command.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;reload:&lt;/code&gt;: This line declares the &lt;code&gt;reload&lt;/code&gt; target. When you run &lt;code&gt;make reload&lt;/code&gt;, it will execute the command listed below.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;postfix reload&lt;/code&gt;: This line is the command to reload the Postfix service. It tells Postfix to reload its configuration, applying any changes that may have been made.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Summing up, when you run &lt;code&gt;make&lt;/code&gt;, it will generate or update the necessary database files for Postfix configuration (&lt;code&gt;aliases.db&lt;/code&gt;, &lt;code&gt;access.db&lt;/code&gt;, &lt;code&gt;virtual.db&lt;/code&gt;) and then reload the Postfix service.&lt;/p&gt;

&lt;p&gt;Now you won't make mistakes again.&lt;/p&gt;

&lt;p&gt;I know that today many of you would say: "just use Ansible dude!" But at that time, there was no Ansible yet. I didn't use this pattern for years now, so let check more up to date usage examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use of &lt;code&gt;make&lt;/code&gt; in Python projects
&lt;/h2&gt;

&lt;p&gt;I love Python for it's simplicity... at least when it comes to coding, because when you start managing dependencies, it's getting tricky. What do you use: raw &lt;code&gt;dependencies.txt&lt;/code&gt; or rather &lt;a href="https://python-poetry.org/" rel="noopener noreferrer"&gt;Poetry&lt;/a&gt; or &lt;a href="https://pipenv.pypa.io/en/latest/" rel="noopener noreferrer"&gt;Pipenv&lt;/a&gt;? Do you use system Python or maybe &lt;a href="https://github.com/pyenv/pyenv" rel="noopener noreferrer"&gt;&lt;code&gt;pyenv&lt;/code&gt;&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;My answer: it depends 😃&lt;/p&gt;

&lt;p&gt;For simple projects, I usually just use &lt;code&gt;pip&lt;/code&gt;. Sometimes even without &lt;code&gt;requirements.txt&lt;/code&gt; files, just listing in the README what to install. But the more projects I wrote, the harder it is to remember how to test them. Again, that's where Make comes handy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;requirements test&lt;/span&gt;

&lt;span class="nl"&gt;.venv&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv

&lt;span class="nl"&gt;requirements&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;pytest

&lt;span class="nl"&gt;test&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;.venv requirements dev-requirements&lt;/span&gt;
    &lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        pytest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens here?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;.PHONY: requirements test&lt;/code&gt;: Declares &lt;code&gt;requirements&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt; as phony targets to ensure they are always executed regardless of file existence.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;.venv:&lt;/code&gt;: Creates a Python virtual environment named &lt;code&gt;.venv&lt;/code&gt; if it doesn't already exist.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;requirements:&lt;/code&gt;: Installs Python packages listed in &lt;code&gt;requirements.txt&lt;/code&gt; into the virtual environment created earlier. Additionally, it installs the &lt;code&gt;pytest&lt;/code&gt; package globally.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;test: .venv requirements dev-requirements&lt;/code&gt;: Sets up dependencies for testing, including the virtual environment and specified requirements. Then, it activates the virtual environment and runs the tests using &lt;code&gt;pytest&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Alternative config for Poetry, might look more or less like that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;requirements test&lt;/span&gt;

&lt;span class="nl"&gt;requirements&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    poetry &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="nl"&gt;test&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;requirements&lt;/span&gt;
    poetry run pytest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I see &lt;code&gt;Makefile&lt;/code&gt; in a Python project, I can blindly run &lt;code&gt;make test&lt;/code&gt; and it will do what I expect -&amp;gt; run tests. Whatever it requires to configure or run, it will just happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;make&lt;/code&gt; in Terraform projects
&lt;/h2&gt;

&lt;p&gt;Similar situation to Python, I have with &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; projects. In simple project you just need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init
terraform plan
terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what if you use different accounts for PROD and DEV environments? What if you need to fetch latest version of modules?&lt;/p&gt;

&lt;p&gt;I have a &lt;code&gt;Makefile&lt;/code&gt; for this too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;
&lt;span class="nv"&gt;SHELL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/bin/bash

&lt;span class="c"&gt;# those variables you should initialize outside of this script
# and export, Make will just set then based on what you will
# have set in your environment. You can use for eg. `aws sts`
&lt;/span&gt;&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt; &lt;span class="o"&gt;?=&lt;/span&gt;
&lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt; &lt;span class="o"&gt;?=&lt;/span&gt;
&lt;span class="nv"&gt;AWS_REGION&lt;/span&gt; &lt;span class="o"&gt;?=&lt;/span&gt; &lt;span class="s2"&gt;"us-west-2"&lt;/span&gt;

&lt;span class="c"&gt;# dev by default
&lt;/span&gt;&lt;span class="nv"&gt;ENVIRONMENT&lt;/span&gt; &lt;span class="o"&gt;?=&lt;/span&gt; dev
&lt;span class="nv"&gt;STATE_FILE_BUCKET&lt;/span&gt; &lt;span class="o"&gt;?=&lt;/span&gt; s3-bucket-&lt;span class="p"&gt;$(&lt;/span&gt;AWS_ACCESS_KEY_ID&lt;span class="p"&gt;)&lt;/span&gt;-&lt;span class="p"&gt;$(&lt;/span&gt;ENVIRONMENT&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nt"&gt;-terraform-state&lt;/span&gt;
&lt;span class="nv"&gt;STATE_FILE_KEY&lt;/span&gt; &lt;span class="o"&gt;?=&lt;/span&gt; state/some_service/&lt;span class="p"&gt;$(&lt;/span&gt;ENVIRONMENT&lt;span class="p"&gt;)&lt;/span&gt;/terraform.tfstate

&lt;span class="c"&gt;# make some variable available in Terraform
&lt;/span&gt;&lt;span class="k"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TF_VAR_something&lt;/span&gt; &lt;span class="o"&gt;?=&lt;/span&gt; something1
&lt;span class="k"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TF_VAR_something_else&lt;/span&gt; &lt;span class="o"&gt;?=&lt;/span&gt; something-else

&lt;span class="nl"&gt;.terraform&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    terraform init &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-reconfigure&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-backend-config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'key=&lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;&lt;span class="s1"&gt;STATE_FILE_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-backend-config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'bucket=&lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;&lt;span class="s1"&gt;STATE_FILE_BUCKET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-var-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;environments/&lt;span class="p"&gt;$(&lt;/span&gt;ENVIRONMENT&lt;span class="p"&gt;)&lt;/span&gt;/variables.tfvars &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-out&lt;/span&gt; terraform.plan

    &lt;span class="err"&gt;terraform&lt;/span&gt; &lt;span class="err"&gt;get&lt;/span&gt;

&lt;span class="c"&gt;# this will switch Terraform version to the one that your project needs
# https://github.com/tfutils/tfenv
&lt;/span&gt;&lt;span class="nl"&gt;init&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;.terraform&lt;/span&gt;
    tfenv &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="nl"&gt;plan&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;
    terraform plan

&lt;span class="nl"&gt;apply&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;plan&lt;/span&gt;
    terraform apply &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-auto-approve&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        terraform.plan

&lt;span class="nl"&gt;destroy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    terraform destroy &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-auto-approve&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-var-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;environments/&lt;span class="p"&gt;$(&lt;/span&gt;ENVIRONMENT&lt;span class="p"&gt;)&lt;/span&gt;/variables.tfvars

&lt;span class="nl"&gt;dev-plan&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;export AWS_ACCESS_KEY_ID=dev-key&lt;/span&gt;
&lt;span class="nl"&gt;dev-plan&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;plan&lt;/span&gt;

&lt;span class="nl"&gt;dev-apply&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;export AWS_ACCESS_KEY_ID=dev-key&lt;/span&gt;
&lt;span class="nl"&gt;dev-apply&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;

&lt;span class="nl"&gt;dev-destroy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;export AWS_ACCESS_KEY_ID=dev-key&lt;/span&gt;
&lt;span class="nl"&gt;dev-destroy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;destroy&lt;/span&gt;

&lt;span class="nl"&gt;prod-plan&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;export AWS_ACCESS_KEY_ID=prod-key&lt;/span&gt;
&lt;span class="nl"&gt;prod-plan&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;plan&lt;/span&gt;

&lt;span class="nl"&gt;prod-apply&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;export AWS_ACCESS_KEY_ID=prod-key&lt;/span&gt;
&lt;span class="nl"&gt;prod-apply&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;

&lt;span class="nl"&gt;clean&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; .terraform/modules
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; terraform.&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file expects a directory structure like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tree example/
&lt;span class="nb"&gt;.&lt;/span&gt;
├── main.tf
├── variables.tf
├── provider.tf
├── backend.tf
├── outputs.tf
├── ...
├── environments/
│   ├── dev
│   │   ├── variables.tfars
│   ├── prod/
│   │   ├── variables.tfars
│   ├── .../
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Backends configuration in the &lt;code&gt;backend.tf&lt;/code&gt; file can be just basic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"s3"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-west-2"&lt;/span&gt;
        &lt;span class="nx"&gt;encrypt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rest of parameters are provided in the Makefile - it's called partial backend configuration&lt;sup id="fnref4"&gt;4&lt;/sup&gt;. This configuration allows me to use same codebase for all the environments. All customizations have to be listed as variables in &lt;code&gt;variables.tfvars&lt;/code&gt; files. It can be easily extended to suport 4 or 6 environments and the only thing I need to remember is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make dev-plan
make dev-apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;code&gt;make&lt;/code&gt; for Hugo blogging
&lt;/h2&gt;

&lt;p&gt;Even for blogging with Hugo I have a &lt;code&gt;Makefile&lt;/code&gt;&lt;sup id="fnref5"&gt;5&lt;/sup&gt; that I use across multiple sites. It's simplifying some of the steps, that I won't need to remember anymore.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nv"&gt;BASEDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;CURDIR&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;INPUTDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;BASEDIR&lt;span class="p"&gt;)&lt;/span&gt;/content
&lt;span class="nv"&gt;STATICDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;BASEDIR&lt;span class="p"&gt;)&lt;/span&gt;/static
&lt;span class="nv"&gt;OUTPUTDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;BASEDIR&lt;span class="p"&gt;)&lt;/span&gt;/public
&lt;span class="nv"&gt;RESOURCESDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;BASEDIR&lt;span class="p"&gt;)&lt;/span&gt;/resources
&lt;span class="nv"&gt;PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1313

&lt;span class="nv"&gt;FTP_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;localhost
&lt;span class="nv"&gt;FTP_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;anonymous
&lt;span class="nv"&gt;FTP_TARGET_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/

&lt;span class="nv"&gt;SSH_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vc1
&lt;span class="nv"&gt;SSH_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;22
&lt;span class="nv"&gt;SSH_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;root
&lt;span class="nv"&gt;SSH_TARGET_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/www/hugo
&lt;span class="nv"&gt;SSH_CHOWN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;33:33

&lt;span class="nv"&gt;S3_BUCKET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my_s3_bucket

&lt;span class="nv"&gt;CLOUDFILES_USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my_rackspace_username
&lt;span class="nv"&gt;CLOUDFILES_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my_rackspace_api_key
&lt;span class="nv"&gt;CLOUDFILES_CONTAINER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my_cloudfiles_container

&lt;span class="nv"&gt;DROPBOX_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/Dropbox/Public/

&lt;span class="nv"&gt;GITHUB_PAGES_BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gh-pages

&lt;span class="nl"&gt;all&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;html&lt;/span&gt;

&lt;span class="nl"&gt;publish&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;html gzip_static rsync_upload&lt;/span&gt;

&lt;span class="nl"&gt;help&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Makefile for a hugo Web site                                              '&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'                                                                          '&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Usage:                                                                    '&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'   make html                           (re)generate the web site          '&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'   make clean                          remove the generated files         '&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'   make publish                        generate using production settings '&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'   make server [PORT=1313]             serve site at http://localhost:1313'&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'   make ssh_upload                     upload the web site via SSH        '&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'   make rsync_upload                   upload the web site via rsync+ssh  '&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'   make dropbox_upload                 upload the web site via Dropbox    '&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'   make ftp_upload                     upload the web site via FTP        '&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'   make s3_upload                      upload the web site via S3         '&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'   make cf_upload                      upload the web site via Cloud Files'&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'   make github                         upload the web site via gh-pages   '&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'                                                                          '&lt;/span&gt;

&lt;span class="nl"&gt;html&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;clean&lt;/span&gt;
    hugo &lt;span class="nt"&gt;--minify&lt;/span&gt;

&lt;span class="nl"&gt;clean&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="p"&gt;)&lt;/span&gt;/.placeholder
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;RESOURCESDIR&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nl"&gt;server&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="k"&gt;ifdef&lt;/span&gt; &lt;span class="nv"&gt;PORT&lt;/span&gt;
    hugo server &lt;span class="nt"&gt;-D&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;PORT&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;--disableFastRender&lt;/span&gt; &lt;span class="nt"&gt;--buildExpired&lt;/span&gt; &lt;span class="nt"&gt;--buildFuture&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
    hugo server &lt;span class="nt"&gt;-D&lt;/span&gt; &lt;span class="nt"&gt;--disableFastRender&lt;/span&gt; &lt;span class="nt"&gt;--buildExpired&lt;/span&gt; &lt;span class="nt"&gt;--buildFuture&lt;/span&gt;
&lt;span class="k"&gt;endif&lt;/span&gt;

&lt;span class="nl"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;clean&lt;/span&gt;
    &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;BASEDIR&lt;span class="p"&gt;);&lt;/span&gt; hugo

&lt;span class="nl"&gt;check_urls&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /tmp&lt;span class="p"&gt;;&lt;/span&gt; wget &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;--spider&lt;/span&gt; http://localhost:&lt;span class="p"&gt;$(&lt;/span&gt;PORT&lt;span class="p"&gt;)&lt;/span&gt; 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-B&lt;/span&gt; 2 &lt;span class="s2"&gt;"404 Not Found"&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;http:// | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;" "&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 4 | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;

&lt;span class="nl"&gt;markdownlint&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="p"&gt;${&lt;/span&gt;PWD&lt;span class="p"&gt;}&lt;/span&gt;:/data:ro markdownlint/markdownlint content

&lt;span class="nl"&gt;gzip_static&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;pattern &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"*.js"&lt;/span&gt; &lt;span class="s2"&gt;"*.json"&lt;/span&gt; &lt;span class="s2"&gt;"*.css"&lt;/span&gt; &lt;span class="s2"&gt;"*.htm"&lt;/span&gt; &lt;span class="s2"&gt;"*.html"&lt;/span&gt; &lt;span class="s2"&gt;"*.xml"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        find &lt;span class="p"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-iname&lt;/span&gt; &lt;span class="nv"&gt;$$&lt;/span&gt;pattern &lt;span class="nt"&gt;-print0&lt;/span&gt; | xargs &lt;span class="nt"&gt;-0&lt;/span&gt; &lt;span class="nt"&gt;-I&lt;/span&gt;&lt;span class="s1"&gt;'{}'&lt;/span&gt; sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'gzip -c9 "{}" &amp;gt; "{}.gz" &amp;amp;&amp;amp; touch -r "{}" "{}.gz"'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="nl"&gt;optimize_images&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    find &lt;span class="p"&gt;$(&lt;/span&gt;STATICDIR&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-mtime&lt;/span&gt; &lt;span class="nt"&gt;-7&lt;/span&gt; &lt;span class="nt"&gt;-iname&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;.png &lt;span class="nt"&gt;-print&lt;/span&gt; | parallel optipng &lt;span class="nt"&gt;-quiet&lt;/span&gt; &lt;span class="nt"&gt;-preserve&lt;/span&gt; &lt;span class="nt"&gt;-o7&lt;/span&gt;
    find &lt;span class="p"&gt;$(&lt;/span&gt;INPUTDIR&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="nt"&gt;-mtime&lt;/span&gt; &lt;span class="nt"&gt;-7&lt;/span&gt; &lt;span class="nt"&gt;-iname&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;.png &lt;span class="nt"&gt;-print&lt;/span&gt; | parallel optipng &lt;span class="nt"&gt;-quiet&lt;/span&gt; &lt;span class="nt"&gt;-preserve&lt;/span&gt; &lt;span class="nt"&gt;-o7&lt;/span&gt;
    find &lt;span class="p"&gt;$(&lt;/span&gt;STATICDIR&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-mtime&lt;/span&gt; &lt;span class="nt"&gt;-7&lt;/span&gt; &lt;span class="nt"&gt;-iname&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;.jpg &lt;span class="nt"&gt;-print&lt;/span&gt; | parallel jpegtran &lt;span class="nt"&gt;-optimize&lt;/span&gt; &lt;span class="nt"&gt;-progressive&lt;/span&gt; &lt;span class="nt"&gt;-copy&lt;/span&gt; none &lt;span class="nt"&gt;-outfile&lt;/span&gt; &lt;span class="s2"&gt;"{}"&lt;/span&gt; &lt;span class="s2"&gt;"{}"&lt;/span&gt;
    find &lt;span class="p"&gt;$(&lt;/span&gt;INPUTDIR&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="nt"&gt;-mtime&lt;/span&gt; &lt;span class="nt"&gt;-7&lt;/span&gt; &lt;span class="nt"&gt;-iname&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;.jpg &lt;span class="nt"&gt;-print&lt;/span&gt; | parallel jpegtran &lt;span class="nt"&gt;-optimize&lt;/span&gt; &lt;span class="nt"&gt;-progressive&lt;/span&gt; &lt;span class="nt"&gt;-copy&lt;/span&gt; none &lt;span class="nt"&gt;-outfile&lt;/span&gt; &lt;span class="s2"&gt;"{}"&lt;/span&gt; &lt;span class="s2"&gt;"{}"&lt;/span&gt;

&lt;span class="nl"&gt;ssh_upload&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;
    scp &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;SSH_PORT&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="p"&gt;)&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;SSH_USER&lt;span class="p"&gt;)&lt;/span&gt;@&lt;span class="p"&gt;$(&lt;/span&gt;SSH_HOST&lt;span class="p"&gt;)&lt;/span&gt;:&lt;span class="p"&gt;$(&lt;/span&gt;SSH_TARGET_DIR&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nl"&gt;rsync_upload&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;generate gzip_static&lt;/span&gt;
&lt;span class="k"&gt;ifdef&lt;/span&gt; &lt;span class="nv"&gt;SSH_CHOWN&lt;/span&gt;
    rsync &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"ssh -p &lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;&lt;span class="s2"&gt;SSH_PORT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="nt"&gt;-avh&lt;/span&gt; &lt;span class="nt"&gt;--delete&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="p"&gt;)&lt;/span&gt;/ &lt;span class="p"&gt;$(&lt;/span&gt;SSH_USER&lt;span class="p"&gt;)&lt;/span&gt;@&lt;span class="p"&gt;$(&lt;/span&gt;SSH_HOST&lt;span class="p"&gt;)&lt;/span&gt;:&lt;span class="p"&gt;$(&lt;/span&gt;SSH_TARGET_DIR&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;--chown&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;SSH_CHOWN&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
    rsync &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"ssh -p &lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;&lt;span class="s2"&gt;SSH_PORT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="nt"&gt;-avh&lt;/span&gt; &lt;span class="nt"&gt;--delete&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="p"&gt;)&lt;/span&gt;/ &lt;span class="p"&gt;$(&lt;/span&gt;SSH_USER&lt;span class="p"&gt;)&lt;/span&gt;@&lt;span class="p"&gt;$(&lt;/span&gt;SSH_HOST&lt;span class="p"&gt;)&lt;/span&gt;:&lt;span class="p"&gt;$(&lt;/span&gt;SSH_TARGET_DIR&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;endif&lt;/span&gt;

&lt;span class="nl"&gt;dropbox_upload&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;
    &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="p"&gt;)&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;DROPBOX_DIR&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nl"&gt;ftp_upload&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;
    lftp ftp://&lt;span class="p"&gt;$(&lt;/span&gt;FTP_USER&lt;span class="p"&gt;)&lt;/span&gt;@&lt;span class="p"&gt;$(&lt;/span&gt;FTP_HOST&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"mirror -R &lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;&lt;span class="s2"&gt;OUTPUTDIR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;&lt;span class="s2"&gt;FTP_TARGET_DIR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; ; quit"&lt;/span&gt;

&lt;span class="nl"&gt;s3_upload&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;
    s3cmd &lt;span class="nb"&gt;sync&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="p"&gt;)&lt;/span&gt;/ s3://&lt;span class="p"&gt;$(&lt;/span&gt;S3_BUCKET&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;--acl-public&lt;/span&gt; &lt;span class="nt"&gt;--delete-removed&lt;/span&gt; &lt;span class="nt"&gt;--guess-mime-type&lt;/span&gt;

&lt;span class="nl"&gt;cf_upload&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;
    &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; swift &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; https://auth.api.rackspacecloud.com/v1.0 &lt;span class="nt"&gt;-U&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;CLOUDFILES_USERNAME&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-K&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;CLOUDFILES_API_KEY&lt;span class="p"&gt;)&lt;/span&gt; upload &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;CLOUDFILES_CONTAINER&lt;span class="p"&gt;)&lt;/span&gt; .

&lt;span class="nl"&gt;github&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;
    &lt;span class="c"&gt;# ghp-import -m "Generate Hugo site" -b &lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;GITHUB_PAGES_BRANCH&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# git push origin &lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;GITHUB_PAGES_BRANCH&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="p"&gt;)&lt;/span&gt;
    git add &lt;span class="nt"&gt;--all&lt;/span&gt;
    git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Update"&lt;/span&gt;
    git push

&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;all html help clean generate server ssh_upload rsync_upload dropbox_upload ftp_upload s3_upload cf_upload github&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Makefile is actually &lt;a href="https://github.com/getpelican/pelican-blog/blob/main/Makefile" rel="noopener noreferrer"&gt;an extension&lt;/a&gt; of one dedicated &lt;a href="https://getpelican.com/" rel="noopener noreferrer"&gt;Pelican&lt;/a&gt; static page generator&lt;/p&gt;

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

&lt;p&gt;There are many creative ways to use &lt;code&gt;Makefiles&lt;/code&gt; to automate and simplify daily tasks. Tool is small and simple, available on any platform (even on Windows via WSL or &lt;a href="https://www.cygwin.com/" rel="noopener noreferrer"&gt;Cygwin&lt;/a&gt;). Many of my colleagues considered this tool an "old school" or "obsolete" initially, but they eventually fall under impression of the recipes simplicity and now just replicate them all around.&lt;/p&gt;

&lt;p&gt;I hope, I will also impress you 😉 Good luck and happy automating!&lt;/p&gt;







&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://www.gnu.org/software/make/" rel="noopener noreferrer"&gt;https://www.gnu.org/software/make/&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;a href="https://www.postfix.org/DATABASE_README.html" rel="noopener noreferrer"&gt;https://www.postfix.org/DATABASE_README.html&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;&lt;a href="https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html" rel="noopener noreferrer"&gt;https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;&lt;a href="https://developer.hashicorp.com/terraform/language/settings/backends/configuration#partial-configuration" rel="noopener noreferrer"&gt;https://developer.hashicorp.com/terraform/language/settings/backends/configuration#partial-configuration&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;&lt;a href="https://github.com/getpelican/pelican-blog/blob/main/Makefile" rel="noopener noreferrer"&gt;https://github.com/getpelican/pelican-blog/blob/main/Makefile&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>linux</category>
      <category>bash</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
