<![CDATA[Just another fancy blog.]]>https://sawczuk.devGatsbyJSSun, 04 Aug 2024 21:50:48 GMT<![CDATA[Setting Up a Terragrunt with tfEnv, SOPS, TFLint, and pre-commit]]>https://sawczuk.dev/posts/setting-up-a-terragrunthttps://sawczuk.dev/posts/setting-up-a-terragruntSun, 04 Aug 2024 17:49:09 GMT<p>Setting up a Terragrunt repository effectively maybe it’s hard, but it’s crucial for maintaining a clean, secure, and efficient infrastructure-as-code workflow. With this post, I wanted to share a quick tips, how in project maintained by me, through few years of experimentation I was able to structuring Terragrunt repository and configuring it with SOPS for secrets management, TFLint for Terraform linting, Pre-commit hooks for maintaining code quality, Tofuutils/Tenv for environment management and more.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/000c3d3fbfd1f05ba679b9f2342afb08/47311/server-4.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIEAwX/xAAVAQEBAAAAAAAAAAAAAAAAAAABAP/aAAwDAQACEAMQAAAB5FcrDmIR/8QAGxABAAAHAAAAAAAAAAAAAAAAAQAEEBITMkH/2gAIAQEAAQUCtiXDJ11Kf//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABoQAAICAwAAAAAAAAAAAAAAAAABEBEhMYH/2gAIAQEABj8Czotwjkf/xAAcEAEAAQQDAAAAAAAAAAAAAAABACExQXERUbH/2gAIAQEAAT8h7I0zDBgOMxqmt54JYbRxP//aAAwDAQACAAMAAAAQuD//xAAXEQEAAwAAAAAAAAAAAAAAAAABEBEh/9oACAEDAQE/EErY/8QAFhEBAQEAAAAAAAAAAAAAAAAAAREQ/9oACAECAQE/EBrM/8QAHhABAAIBBAMAAAAAAAAAAAAAAQARQRAhMbFxocH/2gAIAQEAAT8QCFICjKCdJc6ySlUrTt5nrdHSnV9n/9k='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/000c3d3fbfd1f05ba679b9f2342afb08/8ac56/server-4.webp 240w, /static/000c3d3fbfd1f05ba679b9f2342afb08/d3be9/server-4.webp 480w, /static/000c3d3fbfd1f05ba679b9f2342afb08/e46b2/server-4.webp 960w, /static/000c3d3fbfd1f05ba679b9f2342afb08/260c2/server-4.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/000c3d3fbfd1f05ba679b9f2342afb08/09b79/server-4.jpg 240w, /static/000c3d3fbfd1f05ba679b9f2342afb08/7cc5e/server-4.jpg 480w, /static/000c3d3fbfd1f05ba679b9f2342afb08/6a068/server-4.jpg 960w, /static/000c3d3fbfd1f05ba679b9f2342afb08/47311/server-4.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/000c3d3fbfd1f05ba679b9f2342afb08/6a068/server-4.jpg" alt="Setting Up a Terragrunt with tfEnv, SOPS, TFLint, and pre-commit" title="Setting Up a Terragrunt with tfEnv, SOPS, TFLint, and pre-commit" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <h2 id="repository-structure" style="position:relative;"><a href="#repository-structure" aria-label="repository structure permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Repository Structure</h2> <p>First things, firsts. Before we start going through entire process of setting up full scale Terraform project, I want to point, what additional programs and wrappers I use in my daily projects.</p> <p>Recommended stack at start of the project:</p> <ul> <li><a href="https://github.com/tfutils/tfenv" target="_blank" rel="nofollow noopener noreferrer">tfenv</a> / <a href="https://github.com/tofuutils/tenv" target="_blank" rel="nofollow noopener noreferrer">tenv</a> for keeping same version of terraform in entire project</li> <li><a href="https://github.com/gruntwork-io/terragrunt" target="_blank" rel="nofollow noopener noreferrer">terragrunt</a> is a state and variables maintaining terraform wrapper</li> <li><a href="https://github.com/terraform-linters/tflint" target="_blank" rel="nofollow noopener noreferrer">tflint</a> for Terraform modules linting</li> <li><a href="https://github.com/aquasecurity/tfsec" target="_blank" rel="nofollow noopener noreferrer">tfsec</a> to keep the eye for any dangerous missed/misused resource configuration</li> <li><a href="https://github.com/getsops/sops" target="_blank" rel="nofollow noopener noreferrer">sops</a> because nobody is perfect and some variables can’t be stored in plain-text</li> <li><a href="https://github.com/pre-commit/pre-commit" target="_blank" rel="nofollow noopener noreferrer">pre-commit</a> to make sure, that pushed changes are not malformed or need correcting after upload</li> </ul> <p>Having that in mind, here’s an overview of the recommended repository structure:</p> <div class="gatsby-highlight" data-language="terraform"><pre class="language-terraform"><code class="language-terraform">. (root) ├── .pre-commit-config.yaml ├── .tflint.hcl ├── modules │ ├── resource-group │ │ ├── main.tf │ │ ├── variables.tf │ │ └── output.tf ├── subscriptions │ ├── .terraform-version │ ├── global.hcl │ ├── sops.global.enc.yml │ ├── terragrunt.hcl │ ├── nonprd │ │ ├── sub.hcl │ │ ├── sops.sub.enc.yml │ │ ├── dev │ │ │ ├── environment.hcl │ │ │ ├── sops.environment.enc.yml │ │ │ └── resource-group │ │ │ └── terragrunt.hcl │ │ └── tst │ │ ├── environment.hcl │ │ ├── sops.environment.enc.yml │ │ └── resource-group │ │ └── terragrunt.hcl</code></pre></div> <h2 id="setting-up-basics" style="position:relative;"><a href="#setting-up-basics" aria-label="setting up basics permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Setting up basics</h2> <p>For start we need to create few files and folders to ensure, that future state maintaining through Terragrunt and sorted by folders tree will reflect future project structure, used regions, environments and other splits of used infrastructure.</p> <div class="gatsby-highlight" data-language="terraform"><pre class="language-terraform"><code class="language-terraform">. (root) ├── modules │ ├── resource-group │ │ ├── main.tf │ │ ├── variables.tf │ │ └── output.tf ├── subscriptions │ ├── global.hcl │ ├── terragrunt.hcl │ ├── nonprd │ │ ├── sub.hcl │ │ └── dev │ │ ├── environment.hcl │ │ └── resource-group │ │ └── terragrunt.hcl</code></pre></div> <h2 id="first-and-most-important-file" style="position:relative;"><a href="#first-and-most-important-file" aria-label="first and most important file permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>First, and most important file</h2> <p>The most important file is surely <code class="language-text">subscriptions/terragrunt.hcl</code>. This file store all necessary information about project, build variable, connect to right cloud provider and keep connect to a state files holding information about current IaC.</p> <p>File itself is split into four parts, each providing different purpose.</p> <ul> <li><code class="language-text">locals</code> take care of finding and using variables spread through project structure</li> <li><code class="language-text">provider</code> take care of the connection to the right project subscription</li> <li><code class="language-text">remote_state</code> is the set of the instructions, where and how to store project terraform state files</li> <li><code class="language-text">inputs</code> provide all read variables as default inputs to simplifies future use of modules in the project</li> </ul> <div class="gatsby-highlight" data-language="hcl"><pre class="language-hcl"><code class="language-hcl"><span class="token keyword">locals</span> <span class="token punctuation">{</span> <span class="token property">global_vars</span> <span class="token punctuation">=</span> read_terragrunt_config(find_in_parent_folders(<span class="token string">"global.hcl"</span>)) <span class="token property">sub_vars</span> <span class="token punctuation">=</span> read_terragrunt_config(find_in_parent_folders(<span class="token string">"sub.hcl"</span>)) <span class="token property">environment_vars</span> <span class="token punctuation">=</span> read_terragrunt_config(find_in_parent_folders(<span class="token string">"environment.hcl"</span>)) <span class="token property">sops_global_map</span> <span class="token punctuation">=</span> try(yamldecode(sops_decrypt_file(find_in_parent_folders(<span class="token string">"sops.global.enc.yml"</span>))), <span class="token punctuation">{</span><span class="token punctuation">}</span>) <span class="token property">sops_sub_map</span> <span class="token punctuation">=</span> try(yamldecode(sops_decrypt_file(find_in_parent_folders(<span class="token string">"sops.sub.enc.yml"</span>))), <span class="token punctuation">{</span><span class="token punctuation">}</span>) <span class="token property">sops_environment_map</span> <span class="token punctuation">=</span> try(yamldecode(sops_decrypt_file(find_in_parent_folders(<span class="token string">"sops.environment.enc.yml"</span>))), <span class="token punctuation">{</span><span class="token punctuation">}</span>) <span class="token property">resource_name_prefix</span> <span class="token punctuation">=</span> <span class="token string">"<span class="token interpolation"><span class="token punctuation">$</span><span class="token punctuation">{</span><span class="token keyword">local</span><span class="token punctuation">.</span><span class="token type variable">global_vars</span><span class="token punctuation">.</span>locals<span class="token punctuation">.</span>project_name<span class="token punctuation">}</span></span>-<span class="token interpolation"><span class="token punctuation">$</span><span class="token punctuation">{</span><span class="token keyword">local</span><span class="token punctuation">.</span><span class="token type variable">environment_vars</span><span class="token punctuation">.</span>environment<span class="token punctuation">}</span></span>"</span> <span class="token punctuation">}</span> generate <span class="token string">"provider"</span> <span class="token punctuation">{</span> <span class="token property">path</span> <span class="token punctuation">=</span> <span class="token string">"provider.tf"</span> <span class="token property">if_exists</span> <span class="token punctuation">=</span> <span class="token string">"overwrite_terragrunt"</span> <span class="token property">contents</span> <span class="token punctuation">=</span> <span class="token heredoc string">&lt;&lt;EOF provider "azurerm" { features {} subscription_id = "${local.sub_vars.locals.subscription_id}" } EOF</span> <span class="token punctuation">}</span> <span class="token keyword">remote_state</span> <span class="token punctuation">{</span> <span class="token property">backend</span> <span class="token punctuation">=</span> <span class="token string">"azurerm"</span> <span class="token property">generate</span> <span class="token punctuation">=</span> <span class="token punctuation">{</span> <span class="token property">path</span> <span class="token punctuation">=</span> <span class="token string">"backend.tf"</span>, <span class="token property">if_exists</span> <span class="token punctuation">=</span> <span class="token string">"overwrite_terragrunt"</span> <span class="token punctuation">}</span> <span class="token property">config</span> <span class="token punctuation">=</span> <span class="token punctuation">{</span> <span class="token property">subscription_id</span> <span class="token punctuation">=</span> local.sub_vars.locals.subscription_id <span class="token property">resource_group_name</span> <span class="token punctuation">=</span> local.sub_vars.locals.terraform_resource_group <span class="token property">storage_account_name</span> <span class="token punctuation">=</span> local.sub_vars.locals.storage_account <span class="token property">container_name</span> <span class="token punctuation">=</span> <span class="token string">"terraform"</span> <span class="token property">key</span> <span class="token punctuation">=</span> <span class="token string">"<span class="token interpolation"><span class="token punctuation">$</span><span class="token punctuation">{</span><span class="token function">path_relative_to_include</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span></span>/terraform.tfstate"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token property">inputs</span> <span class="token punctuation">=</span> merge( local.global_vars.locals, local.sub_vars.locals, local.environment_vars.locals, local.sops_global_map, local.sops_sub_map, local.sops_environment_map, <span class="token punctuation">{</span> <span class="token property">resource_name_prefix</span> <span class="token punctuation">=</span> local.resource_name_prefix <span class="token property">project_tags</span> <span class="token punctuation">=</span> merge(<span class="token punctuation">{</span> <span class="token property">Environment</span> <span class="token punctuation">=</span> local.environment_vars.environment <span class="token property">IaC</span> <span class="token punctuation">=</span> <span class="token string">"Terraform"</span> <span class="token punctuation">}</span>, lookup(local.subscription_vars.locals, <span class="token string">"subscription_project_tags"</span>, <span class="token punctuation">{</span><span class="token punctuation">}</span>), lookup(local.environment_vars.locals, <span class="token string">"environment_project_tags"</span>, <span class="token punctuation">{</span><span class="token punctuation">}</span>), ) <span class="token punctuation">}</span> )</code></pre></div> <h2 id="specify-the-terraform-version" style="position:relative;"><a href="#specify-the-terraform-version" aria-label="specify the terraform version permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Specify the Terraform version</h2> <p>To ensure consistency across different environments, the best approach is to set one version of used program in the project. For this, it’s recommended to use tfenv or tenv (Fork of tfenv, supporting also OpenTOFU and Windows system). Setting of it is pretty simple. In the <code class="language-text">subscriptions</code> folder, we need to create file <code class="language-text">.terraform-version</code>. This file contains version, that is recommended to use in project. Everytime, that terragrunt use terraform version control, it look at this file and pick up right binary.</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">1.10.0</code></pre></div> <h2 id="variable-files" style="position:relative;"><a href="#variable-files" aria-label="variable files permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Variable files</h2> <p>To keep repeating variables in one place, we need to create those files:</p> <ul> <li><code class="language-text">subscriptions/global.hcl</code> keep configurations that apply to all environments within the repository.</li> <li><code class="language-text">subscriptions/nonprd/sub.hcl</code> Specific configurations for the non-production subscription.</li> <li><code class="language-text">subscriptions/nonprd/dev/environment.hcl</code> Specific configurations for the only this environment.</li> </ul> <p>After loading them into main <code class="language-text">subscriptions/terragrunt.hcl</code>, they are generally viable in every default inputs field of used module. By this case, only additional inputs, that are required for working module are dependencies ones. Good example of variable simplifications is file <code class="language-text">subscriptions/nonprd/dev/resource-group/terragrunt.hcl</code> showed below.</p> <div class="gatsby-highlight" data-language="hcl"><pre class="language-hcl"><code class="language-hcl"><span class="token keyword">include</span> <span class="token punctuation">{</span> <span class="token property">path</span> <span class="token punctuation">=</span> find_in_parent_folders() <span class="token punctuation">}</span> <span class="token keyword">terraform</span> <span class="token punctuation">{</span> <span class="token property">source</span> <span class="token punctuation">=</span> <span class="token string">"../../../../modules//resource-group"</span> <span class="token punctuation">}</span></code></pre></div> <h2 id="using-sops-for-secrets-management" style="position:relative;"><a href="#using-sops-for-secrets-management" aria-label="using sops for secrets management permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Using SOPS for Secrets Management</h2> <p>SOPS is used to manage secrets securely. Encrypted files like <code class="language-text">sops.global.enc.yml</code>, <code class="language-text">sops.sub.enc.yml</code>, and <code class="language-text">sops.environment.enc.yml</code> store sensitive data. To decrypt and use these files, user should usually have permissions to read keyvault keys on provided cloud or could verify its GPG fingerprint registered in SOPS files.</p> <h2 id="maintain-code-quality-with-pre-commit" style="position:relative;"><a href="#maintain-code-quality-with-pre-commit" aria-label="maintain code quality with pre commit permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Maintain code quality with pre-commit</h2> <p>To ensure that code quality checks are performed before any commits, it’s good to setup right git hooks. Provided configuration below ensure, that code is formatted, keep good code structure and integrity, fixing minor git problems, and finally cleaning up cache so even other systems used in projects like ARM, x32, x86 could still run terragrunt commands without any additional added compiler steps.</p> <div class="gatsby-highlight" data-language="yaml"><pre class="language-yaml"><code class="language-yaml"><span class="token key atrule">repos</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">repo</span><span class="token punctuation">:</span> https<span class="token punctuation">:</span>//github.com/antonbabenko/pre<span class="token punctuation">-</span>commit<span class="token punctuation">-</span>terraform <span class="token key atrule">rev</span><span class="token punctuation">:</span> 1.92.1 <span class="token comment"># Get the latest from: https://github.com/antonbabenko/pre-commit-terraform/releases</span> <span class="token key atrule">hooks</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">id</span><span class="token punctuation">:</span> terraform_fmt <span class="token punctuation">-</span> <span class="token key atrule">id</span><span class="token punctuation">:</span> terragrunt_fmt <span class="token comment"># - id: terraform_tfsec # Can be turned on later, in advanced stage of infrastructure</span> <span class="token punctuation">-</span> <span class="token key atrule">id</span><span class="token punctuation">:</span> terraform_tflint <span class="token key atrule">args</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token punctuation">></span><span class="token scalar string"> --args= --color --config=__GIT_WORKING_DIR__/.tflint.hcl</span> <span class="token punctuation">-</span> <span class="token key atrule">repo</span><span class="token punctuation">:</span> https<span class="token punctuation">:</span>//github.com/pre<span class="token punctuation">-</span>commit/pre<span class="token punctuation">-</span>commit<span class="token punctuation">-</span>hooks <span class="token key atrule">rev</span><span class="token punctuation">:</span> v4.6.0 <span class="token key atrule">hooks</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">id</span><span class="token punctuation">:</span> fix<span class="token punctuation">-</span>encoding<span class="token punctuation">-</span>pragma <span class="token punctuation">-</span> <span class="token key atrule">id</span><span class="token punctuation">:</span> destroyed<span class="token punctuation">-</span>symlinks <span class="token punctuation">-</span> <span class="token key atrule">id</span><span class="token punctuation">:</span> check<span class="token punctuation">-</span>yaml <span class="token key atrule">args</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">-</span><span class="token punctuation">-</span>allow<span class="token punctuation">-</span>multiple<span class="token punctuation">-</span>documents<span class="token punctuation">]</span> <span class="token punctuation">-</span> <span class="token key atrule">id</span><span class="token punctuation">:</span> sort<span class="token punctuation">-</span>simple<span class="token punctuation">-</span>yaml <span class="token punctuation">-</span> <span class="token key atrule">id</span><span class="token punctuation">:</span> end<span class="token punctuation">-</span>of<span class="token punctuation">-</span>file<span class="token punctuation">-</span>fixer <span class="token punctuation">-</span> <span class="token key atrule">id</span><span class="token punctuation">:</span> trailing<span class="token punctuation">-</span>whitespace <span class="token punctuation">-</span> <span class="token key atrule">repo</span><span class="token punctuation">:</span> local <span class="token key atrule">hooks</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">id</span><span class="token punctuation">:</span> cache<span class="token punctuation">-</span>clean<span class="token punctuation">-</span>up <span class="token key atrule">name</span><span class="token punctuation">:</span> cache<span class="token punctuation">-</span>clean<span class="token punctuation">-</span>up <span class="token key atrule">entry</span><span class="token punctuation">:</span> bash <span class="token punctuation">-</span>c 'find . <span class="token punctuation">-</span>name ".terragrunt<span class="token punctuation">-</span>cache" <span class="token punctuation">-</span>type d <span class="token punctuation">-</span>print0 <span class="token punctuation">|</span> xargs <span class="token punctuation">-</span>0 /bin/rm <span class="token punctuation">-</span>fR <span class="token important">&amp;&amp;</span> find . <span class="token punctuation">-</span>name ".terraform.lock.hcl" <span class="token punctuation">-</span>type f <span class="token punctuation">-</span>print0 <span class="token punctuation">|</span> xargs <span class="token punctuation">-</span>0 /bin/rm <span class="token punctuation">-</span>fR <span class="token important">&amp;&amp;</span> exit 0' <span class="token key atrule">language</span><span class="token punctuation">:</span> system <span class="token key atrule">types</span><span class="token punctuation">:</span> <span class="token punctuation">[</span>file<span class="token punctuation">]</span> <span class="token key atrule">pass_filenames</span><span class="token punctuation">:</span> <span class="token boolean important">false</span> <span class="token key atrule">always_run</span><span class="token punctuation">:</span> <span class="token boolean important">true</span></code></pre></div> <p>Later, to simply start using it, we can tun this command</p> <div class="gatsby-highlight" data-language="bash"><pre class="language-bash"><code class="language-bash">pre-commit <span class="token function">install</span></code></pre></div> <h2 id="lint-your-terraform-code-according-to-best-practices" style="position:relative;"><a href="#lint-your-terraform-code-according-to-best-practices" aria-label="lint your terraform code according to best practices permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Lint your Terraform code according to best practices</h2> <p>TFLint is a great tool to keep code in good shape. There is a lot of guides how to set and use it, but for me this <code class="language-text">.tflint.hcl</code> settings holding the most sense and not restricting writer too much.</p> <div class="gatsby-highlight" data-language="hcl"><pre class="language-hcl"><code class="language-hcl"><span class="token keyword">config</span> <span class="token punctuation">{</span> <span class="token property">force</span> <span class="token punctuation">=</span> <span class="token boolean">false</span> <span class="token punctuation">}</span> plugin <span class="token string">"azurerm"</span> <span class="token punctuation">{</span> <span class="token property">enabled</span> <span class="token punctuation">=</span> <span class="token boolean">true</span> <span class="token property">version</span> <span class="token punctuation">=</span> <span class="token string">"0.20.0"</span> <span class="token property">source</span> <span class="token punctuation">=</span> <span class="token string">"github.com/terraform-linters/tflint-ruleset-azurerm"</span> <span class="token punctuation">}</span> <span class="token comment"># Disallow deprecated (0.11-style) interpolation</span> rule <span class="token string">"terraform_deprecated_interpolation"</span> <span class="token punctuation">{</span> <span class="token property">enabled</span> <span class="token punctuation">=</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token comment"># Disallow legacy dot index syntax.</span> rule <span class="token string">"terraform_deprecated_index"</span> <span class="token punctuation">{</span> <span class="token property">enabled</span> <span class="token punctuation">=</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token comment"># Disallow variables, data sources, and locals that are declared but never used.</span> rule <span class="token string">"terraform_unused_declarations"</span> <span class="token punctuation">{</span> <span class="token property">enabled</span> <span class="token punctuation">=</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token comment"># Disallow // comments in favor of #.</span> rule <span class="token string">"terraform_comment_syntax"</span> <span class="token punctuation">{</span> <span class="token property">enabled</span> <span class="token punctuation">=</span> <span class="token boolean">false</span> <span class="token punctuation">}</span> <span class="token comment"># Disallow output declarations without description.</span> rule <span class="token string">"terraform_documented_outputs"</span> <span class="token punctuation">{</span> <span class="token property">enabled</span> <span class="token punctuation">=</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token comment"># Disallow variable declarations without description.</span> rule <span class="token string">"terraform_documented_variables"</span> <span class="token punctuation">{</span> <span class="token property">enabled</span> <span class="token punctuation">=</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token comment"># Disallow variable declarations without type.</span> rule <span class="token string">"terraform_typed_variables"</span> <span class="token punctuation">{</span> <span class="token property">enabled</span> <span class="token punctuation">=</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token comment"># Disallow specifying a git or mercurial repository as a module source without pinning to a version.</span> rule <span class="token string">"terraform_module_pinned_source"</span> <span class="token punctuation">{</span> <span class="token property">enabled</span> <span class="token punctuation">=</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token comment"># Enforces naming conventions</span> rule <span class="token string">"terraform_naming_convention"</span> <span class="token punctuation">{</span> <span class="token property">enabled</span> <span class="token punctuation">=</span> <span class="token boolean">true</span> <span class="token keyword">variable</span> <span class="token punctuation">{</span> <span class="token property">format</span> <span class="token punctuation">=</span> <span class="token string">"snake_case"</span> <span class="token punctuation">}</span> <span class="token keyword">locals</span> <span class="token punctuation">{</span> <span class="token property">format</span> <span class="token punctuation">=</span> <span class="token string">"snake_case"</span> <span class="token punctuation">}</span> <span class="token keyword">output</span> <span class="token punctuation">{</span> <span class="token property">format</span> <span class="token punctuation">=</span> <span class="token string">"snake_case"</span> <span class="token punctuation">}</span> <span class="token keyword">resource</span> <span class="token punctuation">{</span> <span class="token property">format</span> <span class="token punctuation">=</span> <span class="token string">"snake_case"</span> <span class="token punctuation">}</span> <span class="token keyword">module</span> <span class="token punctuation">{</span> <span class="token property">format</span> <span class="token punctuation">=</span> <span class="token string">"snake_case"</span> <span class="token punctuation">}</span> <span class="token keyword">data</span> <span class="token punctuation">{</span> <span class="token property">format</span> <span class="token punctuation">=</span> <span class="token string">"snake_case"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment"># Require that all providers have version constraints through required_providers.</span> rule <span class="token string">"terraform_required_providers"</span> <span class="token punctuation">{</span> <span class="token property">enabled</span> <span class="token punctuation">=</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token comment"># Require that all providers are used.</span> rule <span class="token string">"terraform_unused_required_providers"</span> <span class="token punctuation">{</span> <span class="token property">enabled</span> <span class="token punctuation">=</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token comment"># Ensure that a module complies with the Terraform Standard Module Structure</span> rule <span class="token string">"terraform_standard_module_structure"</span> <span class="token punctuation">{</span> <span class="token property">enabled</span> <span class="token punctuation">=</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token comment"># terraform.workspace should not be used with a "remote" backend with remote execution.</span> rule <span class="token string">"terraform_workspace_remote"</span> <span class="token punctuation">{</span> <span class="token property">enabled</span> <span class="token punctuation">=</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token comment"># Disallow terraform declarations without require_version.</span> rule <span class="token string">"terraform_required_version"</span> <span class="token punctuation">{</span> <span class="token property">enabled</span> <span class="token punctuation">=</span> <span class="token boolean">false</span> <span class="token punctuation">}</span></code></pre></div> <h2 id="conclusion" style="position:relative;"><a href="#conclusion" aria-label="conclusion permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Conclusion</h2> <p>By following this structure and using the specified tools, it’s easier to maintain a clean, secure, and efficient Terragrunt repository. Pre-commit hooks ensure code quality, tfEnv keep eye on use of correct terraform version, TFLint enforces best practices, and SOPS manages secrets securely.</p><![CDATA[Trip to Japan: Booking multiple connections tickets]]>https://sawczuk.dev/posts/trip-to-japan-booking-multiple-connections-ticketshttps://sawczuk.dev/posts/trip-to-japan-booking-multiple-connections-ticketsThu, 23 Feb 2023 17:50:34 GMT<p>In few weeks I plan to go take a flight to Japan. But to spare some money, I plan to use multiple connections over direct flights - what it also comes with some disadvantages.</p> <p>Flying from my initial airport in Bangkok (Thailand), I plan to go to Japan via Ho Chi Minh (Vietnam) airport. However, there is a catch in booking system. If you have booked two separate tickets for your journey and need to transit at the Vietnam airport as me, you need to fulfill additional visa requirements.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/36dd03a965c6b811e62deeb3963dd39e/47311/notes-2.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAQGAwX/xAAWAQEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAduhO0TS4wR//8QAGxAAAQQDAAAAAAAAAAAAAAAAAgEDBDEQETL/2gAIAQEAAQUCjO4VrajZclf/xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAwEBPwFn/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGhAAAgIDAAAAAAAAAAAAAAAAAAEQIRESkf/aAAgBAQAGPwLVllY4Kf/EABwQAQABBAMAAAAAAAAAAAAAAAEAECExQRFRof/aAAgBAQABPyEMAc4SOHXculp7j0FS/9oADAMBAAIAAwAAABDgH//EABcRAQEBAQAAAAAAAAAAAAAAAAEAEUH/2gAIAQMBAT8QHYXL/8QAFREBAQAAAAAAAAAAAAAAAAAAAQD/2gAIAQIBAT8QJC//xAAdEAACAgMAAwAAAAAAAAAAAAABIQARMVGxccHw/9oACAEBAAE/EAlslKejKuYoaqXlVwDxPlbmLwOicfc//9k='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/36dd03a965c6b811e62deeb3963dd39e/8ac56/notes-2.webp 240w, /static/36dd03a965c6b811e62deeb3963dd39e/d3be9/notes-2.webp 480w, /static/36dd03a965c6b811e62deeb3963dd39e/e46b2/notes-2.webp 960w, /static/36dd03a965c6b811e62deeb3963dd39e/260c2/notes-2.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/36dd03a965c6b811e62deeb3963dd39e/09b79/notes-2.jpg 240w, /static/36dd03a965c6b811e62deeb3963dd39e/7cc5e/notes-2.jpg 480w, /static/36dd03a965c6b811e62deeb3963dd39e/6a068/notes-2.jpg 960w, /static/36dd03a965c6b811e62deeb3963dd39e/47311/notes-2.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/36dd03a965c6b811e62deeb3963dd39e/6a068/notes-2.jpg" alt="Trip to Japan: Booking multiple connections tickets" title="Trip to Japan: Booking multiple connections tickets" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>Firstly, it is crucial to check the <a href="https://visaguide.world/asia/vietnam-visa/do-i-need-a-visa-for-vietnam/" target="_blank" rel="nofollow noopener noreferrer">visa requirements for Vietnam</a> before you book your tickets. Depending on your nationality and the length of your stay in Vietnam, you may need to apply for a visa in advance or obtain a visa on arrival. It is important to note that if you are transiting through Vietnam without leaving the airport, you may still require a Vietnam Transit Visa ($25 fee). </p> <p>As official articles say, if you are transiting in an airport in Vietnam and omitting all Vietnam Transit Visa acquire problems, you need to fulfill this steps:</p> <ul> <li>You have a ticket for a connecting flight within the next 24 hours</li> <li>You will not leave the airport international transit area</li> <li>You have the necessary documents for your final destination (passport, visa, etc) On the other hand, if your connecting flight is further than 24 hours away, or you would like to enter Vietnam while you wait, you will have to get a visa. <strong>This rule excludes all booked flights booked as separate tickets!</strong></li> </ul> <p>So, when booking separate tickets for your journey, it is important to leave sufficient time for transit between flights. If your first flight is delayed or canceled (happen a lot in SEASIA flights), you may miss your connecting flight by spending crucial time on going through security and immigration checks.</p> <p>Also, important note about luggage: If you have it checked, it is important to confirm with the airline whether your bags will be automatically transferred to your connecting flight or whether you will need to collect them and re-check them - it’s possible that airlines have inner agreement to transfer your luggage between their flights like Vietnam Airlines &#x3C;-> Etihad. If you need to collect and re-check it yourself, you have to go through customs and immigration again, as conveyer belt and international departure zone are on a different levels on airport - requiring you to leave international flight zone.</p> <p>In summary, flying to Japan via a Vietnam airport can be a convenient and cheap option for travelers. However, if you have booked tickets for your journey, it is important to if you need to apply for Vietnam Transit Visa, leave sufficient time for transit, and be prepared for security and immigration checks. By following these tips, you can ensure a smooth travel experience and enjoy your trip.</p><![CDATA[How I discovered, that remote work not only allows you to work from home]]>https://sawczuk.dev/posts/how-i-discovered-that-remote-work-not-only-allows-you-to-work-from-homehttps://sawczuk.dev/posts/how-i-discovered-that-remote-work-not-only-allows-you-to-work-from-homeMon, 26 Dec 2022 17:50:34 GMT<p>It’s been a while since I last posted an update on the blog, but believe me, a lot has changed. For a few months, I’ve been trying to sort out things related to a trip to rather far country. The reason? To visit my friend who decided to move from the Netherlands to other place for a while. The journey… Well, what was originally planned as a quick test of remote work as a 3-week vacation + 3 weeks of remote work, to then return to my home, fell to pieces and turned into quite a long stay for me in the very center foreign country (when all my plans changed to stay here for only 2 months, then 3 months until the Christmas, to maybe 6 months until the end of winter - and as today, to not have plan to return that quickly to my country and thinking how to stay here longer).</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/d316e91175dd98e670b397c4f4df0051/47311/notes-1.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIDBP/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAGcMmkYYl//xAAeEAABAQkAAAAAAAAAAAAAAAACMgABAwQREhMiQ//aAAgBAQABBQKNaI1yMbtuMoo1f//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABsQAAEFAQEAAAAAAAAAAAAAAAEAAhAhgRFx/9oACAEBAAY/AhysQYDRsxqd5H//xAAbEAACAwADAAAAAAAAAAAAAAAAAREhQTGhsf/aAAgBAQABPyF6Mj1SE/jhRag2emOQ/9oADAMBAAIAAwAAABATL//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EAB8QAQACAQMFAAAAAAAAAAAAAAEAIREQMVFBgaGx8P/aAAgBAQABPxBtT6ULgoxtMniRQ0dO7A2AcQ8T0z53Og//2Q=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/d316e91175dd98e670b397c4f4df0051/8ac56/notes-1.webp 240w, /static/d316e91175dd98e670b397c4f4df0051/d3be9/notes-1.webp 480w, /static/d316e91175dd98e670b397c4f4df0051/e46b2/notes-1.webp 960w, /static/d316e91175dd98e670b397c4f4df0051/260c2/notes-1.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/d316e91175dd98e670b397c4f4df0051/09b79/notes-1.jpg 240w, /static/d316e91175dd98e670b397c4f4df0051/7cc5e/notes-1.jpg 480w, /static/d316e91175dd98e670b397c4f4df0051/6a068/notes-1.jpg 960w, /static/d316e91175dd98e670b397c4f4df0051/47311/notes-1.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/d316e91175dd98e670b397c4f4df0051/6a068/notes-1.jpg" alt="How I discovered, that remote work not only allows you to work from home" title="How I discovered, that remote work not only allows you to work from home" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>During this adventure, I met many interesting people. I also have to say that people in this country are very friendly. A longer stay here was also made possible by a closer encounter with culture, much closer to my heart and hobby. Also, my dear partner who makes my life enjoyable. That also could be influenced by just changing rather coldish environments to warmer ones.</p> <p>I was also able to work remotely, despite the initial shift in working hours. I reduce this time by shifting my work hours to different part of the day, which corresponds to classic 7:00 a.m. to 5:00 p.m. office hours in my country. This is a good alternative for me because in the later hours of the day I can concentrate better and perform assigned tasks more quickly. I hope that in the future I will be able to arrange fully asynchronous work - but time will show. At the moment, I am very happy with the current result.</p><![CDATA[Automate Instagram feed with GramAddict]]>https://sawczuk.dev/posts/automate-instagram-feed-with-gramaddicthttps://sawczuk.dev/posts/automate-instagram-feed-with-gramaddictFri, 17 Jun 2022 17:27:13 GMT<p>Sometimes I don’t have time to browse Instagram, and as you know - spent time on your account will definitely help your SEO and getting your online activity noticed by others. Previously, for this purpose, I used to use projects based on the browser in the background, but unfortunately the repository after the Instagram changes died and does not have as many updates (there is also a problem with accepting PR, even if I would like to help in the public repo). </p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/5eca5184e039b5a7aa55283fa3774077/47311/notes-3.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMCBP/EABUBAQEAAAAAAAAAAAAAAAAAAAIB/9oADAMBAAIQAxAAAAG/RmoUQb//xAAYEAADAQEAAAAAAAAAAAAAAAAAAQIxA//aAAgBAQABBQJukRKYsRzJz//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEDAQE/AWf/xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAgEBPwEn/8QAGBABAAMBAAAAAAAAAAAAAAAAAQAQcRL/2gAIAQEABj8COSO0x2v/xAAZEAACAwEAAAAAAAAAAAAAAAABEQAhMRD/2gAIAQEAAT8hTWPacAkWVS4ZI8MQxn//2gAMAwEAAgADAAAAEPT/AP/EABYRAAMAAAAAAAAAAAAAAAAAAAEQIf/aAAgBAwEBPxAov//EABYRAAMAAAAAAAAAAAAAAAAAAAEQIf/aAAgBAgEBPxAav//EAB8QAQACAgAHAAAAAAAAAAAAAAEAESExQWFxobHh8P/aAAgBAQABPxBe0vapzkXhiWCiisYxUcFt9zToJ8XIndPln//Z'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/5eca5184e039b5a7aa55283fa3774077/8ac56/notes-3.webp 240w, /static/5eca5184e039b5a7aa55283fa3774077/d3be9/notes-3.webp 480w, /static/5eca5184e039b5a7aa55283fa3774077/e46b2/notes-3.webp 960w, /static/5eca5184e039b5a7aa55283fa3774077/260c2/notes-3.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/5eca5184e039b5a7aa55283fa3774077/09b79/notes-3.jpg 240w, /static/5eca5184e039b5a7aa55283fa3774077/7cc5e/notes-3.jpg 480w, /static/5eca5184e039b5a7aa55283fa3774077/6a068/notes-3.jpg 960w, /static/5eca5184e039b5a7aa55283fa3774077/47311/notes-3.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/5eca5184e039b5a7aa55283fa3774077/6a068/notes-3.jpg" alt="Automate Instagram feed with GramAddict" title="Automate Instagram feed with GramAddict" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>However, after searching the internet, I found an alternative, which is for me an even better solution and more resistant to failures. I’m talking about <a href="https://github.com/GramAddict/bot" target="_blank" rel="nofollow noopener noreferrer">GramAddict</a>, which uses the debug of the android phone and performs operations directly on device. Using the phone connected to the computer that was executing the script, I admit that it helped me to focus more on my work and not get distracted by the phone. </p> <p>During setup, I sometimes had the problem of the cable disconnecting and losing my data transfer settings (probably broken connectors in phone USB port). This was quite annoying, especially when the device charging itself was left unchanged when the connection was interrupted. The best solution I came up with was to simply connect to the debugger via Wi-Fi, where the phone can now lie in any corner of the house and scroll the feed in peace. </p> <p>Having tested this software for over the month, I would recommend it. GramAddict has lots of configuration options, had no problem with new Instagram media formats and has additional modules for reporting and saving previous operations.</p><![CDATA[Joining #100DaysOfHomeLab Challenge]]>https://sawczuk.dev/posts/joining-100-days-of-homelab-challengehttps://sawczuk.dev/posts/joining-100-days-of-homelab-challengeSun, 12 Jun 2022 22:47:45 GMT<p>At the beginning of the month, following the words of my friend, I started looking into putting up a HomeLab server. After many instances during this short journey, I finally managed to make a server to put up my home services, which I needed for a long time.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/fc3c2d27a44e67535b2c9719bd96391a/47311/server-5.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAMBAgQF/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAHmOvNzjHh//8QAGxAAAgEFAAAAAAAAAAAAAAAAAAECERIhMTL/2gAIAQEAAQUCSMW0FzHR/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGBAAAgMAAAAAAAAAAAAAAAAAAAEgIeH/2gAIAQEABj8CKZsP/8QAGxAAAgMAAwAAAAAAAAAAAAAAAAERITEQcZH/2gAIAQEAAT8hidwNrCxq7Oy8G5px/9oADAMBAAIAAwAAABDj3//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/EFf/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAgEBPxBH/8QAHBABAAICAwEAAAAAAAAAAAAAAQARITEQUaHw/9oACAEBAAE/EAtTKyiVMhQCgw93dMEay/W+P6I7n//Z'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/fc3c2d27a44e67535b2c9719bd96391a/8ac56/server-5.webp 240w, /static/fc3c2d27a44e67535b2c9719bd96391a/d3be9/server-5.webp 480w, /static/fc3c2d27a44e67535b2c9719bd96391a/e46b2/server-5.webp 960w, /static/fc3c2d27a44e67535b2c9719bd96391a/260c2/server-5.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/fc3c2d27a44e67535b2c9719bd96391a/09b79/server-5.jpg 240w, /static/fc3c2d27a44e67535b2c9719bd96391a/7cc5e/server-5.jpg 480w, /static/fc3c2d27a44e67535b2c9719bd96391a/6a068/server-5.jpg 960w, /static/fc3c2d27a44e67535b2c9719bd96391a/47311/server-5.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/fc3c2d27a44e67535b2c9719bd96391a/6a068/server-5.jpg" alt="Joining #100DaysOfHomeLab Challenge" title="Joining #100DaysOfHomeLab Challenge" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>While building the server, in many cases I used tutorials, guides as well as best practice on YouTube. Turns out I also hit on the perfect opportunity to develop my current work with new event. <a href="https://100daysofhomelab.com/" target="_blank" rel="nofollow noopener noreferrer">#100DaysOfHomeLab</a>, an event created by <a href="https://www.youtube.com/watch?v=bwDVW_ifkBU" target="_blank" rel="nofollow noopener noreferrer">Techno Tim</a> as a 100k celebration video, encourages you to spend 1 hour a day for 100 days developing your home environment. For the foreseeable future we may see a collaboration of the largest accounts dedicated to building a HomeLab. Each of the creators with a different approach to technology and the tools used - which can each be adapted to our needs. Along with this event, I have likewise planned new work that will expand my current setup.</p> <p>Planned activities for near future:</p> <ul> <li>adding openHAB</li> <li>automated backups</li> <li>converting HomeLab to k3s</li> </ul> <p>We’ll see how many of these things I can get done. I will also try to give updates from week to week on the development of the project. I think this is a great option for everyone to join in creating their own HomeLab solution. To the next update!</p><![CDATA[Building ad-block home server from the scratch. Again...]]>https://sawczuk.dev/posts/building-rpi-from-the-scratchhttps://sawczuk.dev/posts/building-rpi-from-the-scratchFri, 28 Jan 2022 22:58:33 GMT<p>After the recent chaos I found on my Raspberry Pi server and its unstable performance I made a fresh attempt to build a simple ad filter in my home using rPi.</p> <p>First, remember to change default password of <code class="language-text">pi</code> user!</p> <div class="gatsby-highlight" data-language="shell"><pre class="language-shell"><code class="language-shell"><span class="token function">sudo</span> raspi-config</code></pre></div> <p>Install PiHole from official script, set/reset password for web admin and add auto-update PiHole line to crontab</p> <div class="gatsby-highlight" data-language="shell"><pre class="language-shell"><code class="language-shell"><span class="token function">curl</span> -sSL https://install.pi-hole.net <span class="token operator">|</span> <span class="token function">bash</span> pihole -a -p <span class="token function">crontab</span> -e <span class="token comment"># 0 5 * * * pihole -up</span></code></pre></div> <p>I have to admit that PiHole’s basic blacklists do a good job of filtering ads, but there are places on the web that require more url restriction (like scammer sites). I previously used firebog list for this and manually tried to add lists of blocked sites to the tool. However, as it turns out, someone smart has added their <a href="https://github.com/jessedp/pihole5-list-tool" target="_blank" rel="nofollow noopener noreferrer">python script</a> to automatically add the appropriate list option from both <a href="https://firebog.net/" target="_blank" rel="nofollow noopener noreferrer">firebog</a> (blacklist) and/or <a href="https://github.com/anudeepND/whitelist" target="_blank" rel="nofollow noopener noreferrer">anudeepND/whitelist</a> (whitelist).</p> <p>Also consider adding blocklists focused on your home smart devices:</p> <ul> <li><a href="https://github.com/unknownFalleN/xiaomi-dns-blocklist" target="_blank" rel="nofollow noopener noreferrer">Xiaomi DNS blocklist</a></li> </ul> <div class="gatsby-highlight" data-language="shell"><pre class="language-shell"><code class="language-shell"><span class="token function">sudo</span> pip3 <span class="token function">install</span> pihole5-list-tool --upgrade pihole5-list-tool <span class="token comment"># ┌──────────────────────────────────────────┐</span> <span class="token comment"># │ π-hole 5 list tool v0.6.0 │</span> <span class="token comment"># └──────────────────────────────────────────┘</span> <span class="token comment"># https://github.com/jessedp/pihole5-list-tool</span> <span class="token comment"># </span> <span class="token comment"># ! docker not found running, continuing...</span> <span class="token comment"># </span> <span class="token comment"># ? Gravity Db to Update: /etc/pihole/gravity.db</span> <span class="token comment"># </span> <span class="token comment"># Blocks Enabled: All=0 │ Ours=0 │ Allows Enabled: All=0 │ Ours=0 </span> <span class="token comment"># </span> <span class="token comment"># ? Options: Manage Blocklists</span> <span class="token comment"># ? Blocklist action: Add a list</span> <span class="token comment"># ? Where are the block lists coming from? Firebog | Non-crossed lists : Use when someone is usually around to allow</span> <span class="token comment"># </span> <span class="token comment"># Do not hit ENTER or Y if a step seems to hang!</span> <span class="token comment"># Use CTRL+C if you're sure it's hung and report it.</span> <span class="token comment"># </span> <span class="token comment"># ? Add 45 block lists? Yes</span> <span class="token comment"># 45 block lists added! 0 already existed.</span> <span class="token comment"># Ad/Blocklist Stats │ Top 3 by Comment </span> <span class="token comment"># ───────────────────────┼────────────────────────────────────────────</span> <span class="token comment"># Total : 45/45 │ Firebog | Non-crossed lists [ph5lt] 45 </span> <span class="token comment"># Our Lists : 45/45 │ </span> <span class="token comment"># Others : 0/0 │ </span> <span class="token comment"># │ </span> <span class="token comment"># ? Are you finished? Yes</span> <span class="token comment"># ? Update Gravity for immediate effect? Yes</span></code></pre></div> <p>The final step is to auto-update the rPi itself. I wanted to use a standard crontab with the update+upgrade command to do this, but the problem here may be the occurrence of a dialog windows. A better solution to avoid this problem and update your rPi server with security payloads is to use the <a href="https://wiki.debian.org/UnattendedUpgrades" target="_blank" rel="nofollow noopener noreferrer">UnattendedUpgrades library</a> </p> <div class="gatsby-highlight" data-language="shell"><pre class="language-shell"><code class="language-shell"><span class="token function">apt-get</span> <span class="token function">install</span> unattended-upgrades dpkg-reconfigure -plow unattended-upgrades</code></pre></div> <p>This is the most basic server setup, which should take no more than 30min. But it is also a good basis for future activities with the system that is currently set up and running smoothly. In the future, if I add something as a “base image” for the home server, I will add additional commands in the end of this post.</p><![CDATA[Map all AWS resources and make a vulnerability report using cloudmapper]]>https://sawczuk.dev/posts/map-all-aws-resources-and-make-a-vulnerability-report-using-cloudmapperhttps://sawczuk.dev/posts/map-all-aws-resources-and-make-a-vulnerability-report-using-cloudmapperFri, 28 Jan 2022 21:11:38 GMT<p>Last time I remembered about a tool that allows you to create a big map of current resources present in your AWS account, and based on it make a report of potential vulnerabilities, threats, unused resources and much more. I can thank a colleague at work for finding it - such explorers are a real treasure!</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/0c1a52aeacc5dcaf5e0da19a48c4350d/47311/server-3.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMFBP/EABcBAAMBAAAAAAAAAAAAAAAAAAECAwT/2gAMAwEAAhADEAAAAZw3VXPOGAb/xAAZEAACAwEAAAAAAAAAAAAAAAAAAQIDEUH/2gAIAQEAAQUClEVePcEcP//EABcRAQADAAAAAAAAAAAAAAAAAAABERL/2gAIAQMBAT8B1K3/xAAXEQEAAwAAAAAAAAAAAAAAAAAAAQIR/9oACAECAQE/AYqx/8QAGBAAAwEBAAAAAAAAAAAAAAAAABCBETH/2gAIAQEABj8C1cKRf//EABwQAAIBBQEAAAAAAAAAAAAAAAABESExQWFxUf/aAAgBAQABPyGaiOoqitowNFpmYYe9P//aAAwDAQACAAMAAAAQf+//xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAwEBPxCA/8QAFhEBAQEAAAAAAAAAAAAAAAAAAQAh/9oACAECAQE/EATZn//EAB4QAQACAgIDAQAAAAAAAAAAAAEAESExcbFBYYHw/9oACAEBAAE/EK6KF2Xn1cVAVvhlrdTK1VwDZN+Lufj8hvk7n//Z'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/0c1a52aeacc5dcaf5e0da19a48c4350d/8ac56/server-3.webp 240w, /static/0c1a52aeacc5dcaf5e0da19a48c4350d/d3be9/server-3.webp 480w, /static/0c1a52aeacc5dcaf5e0da19a48c4350d/e46b2/server-3.webp 960w, /static/0c1a52aeacc5dcaf5e0da19a48c4350d/260c2/server-3.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/0c1a52aeacc5dcaf5e0da19a48c4350d/09b79/server-3.jpg 240w, /static/0c1a52aeacc5dcaf5e0da19a48c4350d/7cc5e/server-3.jpg 480w, /static/0c1a52aeacc5dcaf5e0da19a48c4350d/6a068/server-3.jpg 960w, /static/0c1a52aeacc5dcaf5e0da19a48c4350d/47311/server-3.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/0c1a52aeacc5dcaf5e0da19a48c4350d/6a068/server-3.jpg" alt="Map all AWS resources and make a vulnerability report using cloudmapper" title="Map all AWS resources and make a vulnerability report using cloudmapper" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>The <a href="https://github.com/duo-labs/cloudmapper" target="_blank" rel="nofollow noopener noreferrer">duolabs/cloudmapper</a> repository is responsible for scanning and mapping resources on the current user account. It is a great tool to see our mistakes when releasing the environment or purging the client account of unused, often manually created resources. Using the program is best done with building and using a docker image.</p> <div class="gatsby-highlight" data-language="shell"><pre class="language-shell"><code class="language-shell"><span class="token function">git</span> clone https://github.com/duo-labs/cloudmapper.git docker build -t cloudmapper <span class="token builtin class-name">.</span> <span class="token assign-left variable">AWS_PROFILE</span><span class="token operator">=</span>default docker run -it <span class="token punctuation">\</span> -e <span class="token assign-left variable">AWS_ACCESS_KEY_ID</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span>aws configure get aws_access_key_id --profile $<span class="token punctuation">{</span>AWS_PROFILE<span class="token punctuation">}</span><span class="token variable">)</span></span> <span class="token punctuation">\</span> -e <span class="token assign-left variable">AWS_SECRET_ACCESS_KEY</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span>aws configure get aws_secret_access_key --profile $<span class="token punctuation">{</span>AWS_PROFILE<span class="token punctuation">}</span><span class="token variable">)</span></span> <span class="token punctuation">\</span> -p <span class="token number">8000</span>:8000 <span class="token punctuation">\</span> cloudmapper /bin/bash</code></pre></div> <p>Then run this command inside docker shell <code class="language-text">aws sts get-caller-identity</code> to get data about your running account. </p> <div class="gatsby-highlight" data-language="json"><pre class="language-json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"UserId"</span><span class="token operator">:</span> <span class="token string">"JNSNATUSNPWEBAA8HNAPE"</span><span class="token punctuation">,</span> <span class="token property">"Account"</span><span class="token operator">:</span> <span class="token string">"830572105813"</span><span class="token punctuation">,</span> <span class="token property">"Arn"</span><span class="token operator">:</span> <span class="token string">"arn:aws:iam::830572105813:user/project-env"</span> <span class="token punctuation">}</span></code></pre></div> <p>After confirming right data, pass your <code class="language-text">UserId</code> into <code class="language-text">YOUR_ACCOUNT_NUMBER</code> field in first command below (check also <a href="https://github.com/duo-labs/cloudmapper#commands" target="_blank" rel="nofollow noopener noreferrer">additional report commands</a> for unused resources or IAM data)</p> <div class="gatsby-highlight" data-language="shell"><pre class="language-shell"><code class="language-shell">python cloudmapper.py configure add-account --config-file config.json --name YOUR_ACCOUNT --id YOUR_ACCOUNT_NUMBER python cloudmapper.py collect --account YOUR_ACCOUNT python cloudmapper.py report --account YOUR_ACCOUNT python cloudmapper.py prepare --account YOUR_ACCOUNT python cloudmapper.py webserver --public</code></pre></div> <p>After successful data fetch, you can find your results on those links:</p> <ul> <li><a href="http://localhost:8000/" target="_blank" rel="nofollow noopener noreferrer">Resource Map - localhost:8000/</a></li> <li><a href="http://localhost:8000/account-data/report.html" target="_blank" rel="nofollow noopener noreferrer">Report - localhost:8000/account-data/report.html</a></li> </ul> <p>You can find examples of fetched AWS accounts under those links: </p> <ul> <li><a href="https://duo-labs.github.io/cloudmapper/account-data/report.html" target="_blank" rel="nofollow noopener noreferrer">Report</a></li> <li><a href="https://duo-labs.github.io/cloudmapper/" target="_blank" rel="nofollow noopener noreferrer">Resource Map</a></li> <li><a href="https://github.com/duo-labs/cloudmapper/blob/main/auditor/README.md" target="_blank" rel="nofollow noopener noreferrer">daily auto-audits with Slack notifications</a></li> </ul><![CDATA[Secure files using git-crypt]]>https://sawczuk.dev/posts/secure-files-using-git-crypthttps://sawczuk.dev/posts/secure-files-using-git-cryptMon, 20 Dec 2021 23:50:13 GMT<p>There is one major problem when working on a public repository. Anyone can see our access data in our project. It is impossible to hide, at some point, the project may lack another layer securing the data, or even a simple <code class="language-text">.env</code> file with the data required by the project.</p> <p><a href="https://www.agwa.name/projects/git-crypt/" target="_blank" rel="nofollow noopener noreferrer">Git-crypt</a> comes in handy, in which we can encrypt and decrypt files automatically and upload them to the repository with each commit. Each user requires registration and entering their GPG key into the repository. People who do not have the key will not be able to decode the content of the file.</p> <p>Configuration which files we want to encrypt:</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">secretfile filter=git-crypt diff=git-crypt *.key filter=git-crypt diff=git-crypt secretdir/** filter=git-crypt diff=git-crypt</code></pre></div> <p>Initialization and unlocking of the repository:</p> <div class="gatsby-highlight" data-language="shell"><pre class="language-shell"><code class="language-shell">git-crypt init git-crypt add-gpg-user USER_ID git-crypt unlock</code></pre></div><![CDATA[Using Github 'actions']]>https://sawczuk.dev/posts/using-github-actionshttps://sawczuk.dev/posts/using-github-actionsThu, 09 Dec 2021 17:47:29 GMT<p>When creating pipelines, we often use already-made code we’ve written ourselves in previous projects. Whether it is CircleCi, BitBucket Pipelines, AWS CodeBuild or GitHub Actions. The problem is when our cloud environment updates and no longer has the old libraries we used.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/1cc432d60f7f229c9e8cb1c1488fcd85/47311/server-11.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAMEAQL/xAAUAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAGfhupIIB//xAAbEAABBAMAAAAAAAAAAAAAAAABAAIEMRESMv/aAAgBAQABBQIU/AOybxJtf//EABgRAAIDAAAAAAAAAAAAAAAAAAABAhEh/9oACAEDAQE/AZYyz//EABYRAAMAAAAAAAAAAAAAAAAAAAEQMf/aAAgBAgEBPwERf//EABcQAQEBAQAAAAAAAAAAAAAAAAEQEQD/2gAIAQEABj8C3BK8T//EABsQAQEAAgMBAAAAAAAAAAAAAAEAUaEhMUGR/9oACAEBAAE/IVwXyrwEcSDLbZaE93//2gAMAwEAAgADAAAAEEPf/8QAFxEBAAMAAAAAAAAAAAAAAAAAAAERIf/aAAgBAwEBPxDDCz//xAAWEQADAAAAAAAAAAAAAAAAAAAQESH/2gAIAQIBAT8Qph//xAAeEAEAAQQCAwAAAAAAAAAAAAABABEhMXEQoWHB8P/aAAgBAQABPxC0kJeqxdTKFKrVp1HqGzE+R5ndeuL/2Q=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/1cc432d60f7f229c9e8cb1c1488fcd85/8ac56/server-11.webp 240w, /static/1cc432d60f7f229c9e8cb1c1488fcd85/d3be9/server-11.webp 480w, /static/1cc432d60f7f229c9e8cb1c1488fcd85/e46b2/server-11.webp 960w, /static/1cc432d60f7f229c9e8cb1c1488fcd85/260c2/server-11.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/1cc432d60f7f229c9e8cb1c1488fcd85/09b79/server-11.jpg 240w, /static/1cc432d60f7f229c9e8cb1c1488fcd85/7cc5e/server-11.jpg 480w, /static/1cc432d60f7f229c9e8cb1c1488fcd85/6a068/server-11.jpg 960w, /static/1cc432d60f7f229c9e8cb1c1488fcd85/47311/server-11.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/1cc432d60f7f229c9e8cb1c1488fcd85/6a068/server-11.jpg" alt="Using Github &#39;actions&#39;" title="Using Github &#39;actions&#39;" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>This is what happened to my blog repository. It turns out that every time the LTS version of the NodeJS library is released, it is added to the <a href="https://github.com/actions/virtual-environments/issues/1953" target="_blank" rel="nofollow noopener noreferrer">GitHub pipelined image</a>. What caused this? Status failed on my site build pipeline. The solution was to update the packages or lock for the nodejs version. I originally wanted to fix this with another 3 steps in workflow that would allow me to enable the old version of NodeJS (along with <code class="language-text">apt-get update</code> and other commands). The problem is that such steps increase the execution time of the action terribly. Fortunately, it turned out that it was also possible to use the main GitHub Actions feature, the <a href="https://github.com/actions/setup-node" target="_blank" rel="nofollow noopener noreferrer">actions themselves</a>. As it turns out, with the use of simple 3 lines, we can be sure that our environment will have the correct version of NodeJS, and changing it is only one parameter.</p> <div class="gatsby-highlight" data-language="yaml"><pre class="language-yaml"><code class="language-yaml"><span class="token key atrule">steps</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">uses</span><span class="token punctuation">:</span> actions/checkout@v2 <span class="token punctuation">-</span> <span class="token key atrule">uses</span><span class="token punctuation">:</span> actions/setup<span class="token punctuation">-</span>node@v2 <span class="token key atrule">with</span><span class="token punctuation">:</span> <span class="token key atrule">node-version</span><span class="token punctuation">:</span> <span class="token string">'14'</span> <span class="token punctuation">-</span> <span class="token key atrule">run</span><span class="token punctuation">:</span> npm install <span class="token punctuation">-</span> <span class="token key atrule">run</span><span class="token punctuation">:</span> npm test</code></pre></div> <p>The use of template actions is definitely a big advantage of GitHub. It will definitely come in handy for everyone more than once as performing some kind of automation, version lock, testing the environment and much more.</p><![CDATA[Launch multiple WordPress server in docker way]]>https://sawczuk.dev/posts/launch-multiple-wordpress-server-in-docker-wayhttps://sawczuk.dev/posts/launch-multiple-wordpress-server-in-docker-waySun, 21 Nov 2021 19:23:17 GMT<p>If you want to run multiple WordPress sites and other services but don’t have time to configure each one with their specific library versions, you can set all in one big docker config. To run this all, you’ll need an app image itself, reverse proxy preferably with SSL automation and in most cases’ database.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/e675a6d678f5769c36648cfdfe63fd90/47311/server-1.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAQFAgP/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAv/aAAwDAQACEAMQAAABjvzO6ciwr//EABoQAAICAwAAAAAAAAAAAAAAAAECADIDEBL/2gAIAQEAAQUCxXdU5BQQW1//xAAVEQEBAAAAAAAAAAAAAAAAAAAQQf/aAAgBAwEBPwGn/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAHBAAAQMFAAAAAAAAAAAAAAAAAAECERAiQVGR/9oACAEBAAY/AkkudOoMiHKf/8QAGxABAAICAwAAAAAAAAAAAAAAAQAREEFRgZH/2gAIAQEAAT8hpUBGEuXFAFPjG31h/9oADAMBAAIAAwAAABBoz//EABURAQEAAAAAAAAAAAAAAAAAABAx/9oACAEDAQE/EIf/xAAWEQEBAQAAAAAAAAAAAAAAAAABEBH/2gAIAQIBAT8QTSf/xAAdEAACAgEFAAAAAAAAAAAAAAAAAREhYTGhscHw/9oACAEBAAE/EFiwbklh2rTSxSdkKJYT2jfrgWv1Qz//2Q=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/e675a6d678f5769c36648cfdfe63fd90/8ac56/server-1.webp 240w, /static/e675a6d678f5769c36648cfdfe63fd90/d3be9/server-1.webp 480w, /static/e675a6d678f5769c36648cfdfe63fd90/e46b2/server-1.webp 960w, /static/e675a6d678f5769c36648cfdfe63fd90/260c2/server-1.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/e675a6d678f5769c36648cfdfe63fd90/09b79/server-1.jpg 240w, /static/e675a6d678f5769c36648cfdfe63fd90/7cc5e/server-1.jpg 480w, /static/e675a6d678f5769c36648cfdfe63fd90/6a068/server-1.jpg 960w, /static/e675a6d678f5769c36648cfdfe63fd90/47311/server-1.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/e675a6d678f5769c36648cfdfe63fd90/6a068/server-1.jpg" alt="Launch multiple WordPress server in docker way" title="Launch multiple WordPress server in docker way" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>Example below should launch multiple instances of sites, configure them to connect to local database running on host machine, allow reverse-proxy and maintain SSL certificates. </p> <div class="gatsby-highlight" data-language="yaml"><pre class="language-yaml"><code class="language-yaml"><span class="token key atrule">version</span><span class="token punctuation">:</span> <span class="token string">'3'</span> <span class="token key atrule">services</span><span class="token punctuation">:</span> <span class="token comment"># -----------------------------------------------</span> <span class="token comment"># | Set reverse proxy to handle traffic</span> <span class="token comment"># -----------------------------------------------</span> <span class="token key atrule">reverse-proxy</span><span class="token punctuation">:</span> <span class="token key atrule">image</span><span class="token punctuation">:</span> jwilder/nginx<span class="token punctuation">-</span>proxy <span class="token key atrule">ports</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token string">"80:80"</span> <span class="token punctuation">-</span> <span class="token string">"443:443"</span> <span class="token key atrule">volumes</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> ./certs<span class="token punctuation">:</span>/etc/nginx/certs <span class="token punctuation">-</span> /etc/nginx/vhost.d <span class="token punctuation">-</span> /usr/share/nginx/html <span class="token punctuation">-</span> /var/run/docker.sock<span class="token punctuation">:</span>/tmp/docker.sock<span class="token punctuation">:</span>ro <span class="token key atrule">labels</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy <span class="token key atrule">restart</span><span class="token punctuation">:</span> unless<span class="token punctuation">-</span>stopped <span class="token comment"># -----------------------------------------------</span> <span class="token comment"># | Set letsencrypt to menage SSL</span> <span class="token comment"># -----------------------------------------------</span> <span class="token key atrule">letsencrypt-nginx-proxy-companion</span><span class="token punctuation">:</span> <span class="token key atrule">image</span><span class="token punctuation">:</span> jrcs/letsencrypt<span class="token punctuation">-</span>nginx<span class="token punctuation">-</span>proxy<span class="token punctuation">-</span>companion <span class="token key atrule">ports</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token string">"80:80"</span> <span class="token punctuation">-</span> <span class="token string">"443:443"</span> <span class="token key atrule">volumes</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> ./certs<span class="token punctuation">:</span>/etc/nginx/certs<span class="token punctuation">:</span>rw <span class="token punctuation">-</span> /var/run/docker.sock<span class="token punctuation">:</span>/var/run/docker.sock<span class="token punctuation">:</span>ro <span class="token key atrule">volumes_from</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> reverse<span class="token punctuation">-</span>proxy <span class="token key atrule">restart</span><span class="token punctuation">:</span> unless<span class="token punctuation">-</span>stopped <span class="token comment"># -----------------------------------------------</span> <span class="token comment"># | Set services</span> <span class="token comment"># -----------------------------------------------</span> <span class="token key atrule">wordpress_app1_name</span><span class="token punctuation">:</span> <span class="token key atrule">image</span><span class="token punctuation">:</span> wordpress<span class="token punctuation">:</span>php7.2 <span class="token key atrule">environment</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> WORDPRESS_DB_HOST=host.docker.internal<span class="token punctuation">:</span><span class="token number">3306</span> <span class="token punctuation">-</span> WORDPRESS_DB_USER=db1user <span class="token punctuation">-</span> WORDPRESS_DB_PASSWORD=password <span class="token punctuation">-</span> VIRTUAL_HOST=www.app1_host.com <span class="token punctuation">-</span> LETSENCRYPT_HOST=www.app1_host.com <span class="token punctuation">-</span> LETSENCRYPT_EMAIL=confirmation_email@app1_host.com <span class="token key atrule">volumes</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> ./app_1_volume<span class="token punctuation">:</span>/var/www/html <span class="token punctuation">-</span> ./app_1_uploads.ini<span class="token punctuation">:</span>/usr/local/etc/php/conf.d/uploads.ini <span class="token key atrule">restart</span><span class="token punctuation">:</span> unless<span class="token punctuation">-</span>stopped <span class="token key atrule">wordpress_app2_name</span><span class="token punctuation">:</span> <span class="token key atrule">image</span><span class="token punctuation">:</span> wordpress<span class="token punctuation">:</span>php5.6 <span class="token key atrule">environment</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> WORDPRESS_DB_HOST=host.docker.internal<span class="token punctuation">:</span><span class="token number">3306</span> <span class="token punctuation">-</span> WORDPRESS_DB_USER=db1user <span class="token punctuation">-</span> WORDPRESS_DB_PASSWORD=password <span class="token punctuation">-</span> VIRTUAL_HOST=www.app1_host.com <span class="token punctuation">-</span> LETSENCRYPT_HOST=www.app1_host.com <span class="token punctuation">-</span> LETSENCRYPT_EMAIL=confirmation_email@app1_host.com <span class="token key atrule">volumes</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> ./app_2_volume<span class="token punctuation">:</span>/var/www/html <span class="token punctuation">-</span> ./app_2_uploads.ini<span class="token punctuation">:</span>/usr/local/etc/php/conf.d/uploads.ini <span class="token key atrule">restart</span><span class="token punctuation">:</span> unless<span class="token punctuation">-</span>stopped</code></pre></div><![CDATA[Training: Tiny Python Projects]]>https://sawczuk.dev/posts/training-tiny-python-projectshttps://sawczuk.dev/posts/training-tiny-python-projectsSun, 17 Oct 2021 16:48:09 GMT<p>Lastly I found <a href="https://github.com/kyclark/tiny_python_projects" target="_blank" rel="nofollow noopener noreferrer">a small repository</a> related to teaching book about Python. I must say, that even if project is directed to beginners in this language, doing that tasks is refreshing ever for more advanced people. So if you don’t know what to do or want to clear your mind, you can always go to <a href="http://tinypythonprojects.com/" target="_blank" rel="nofollow noopener noreferrer">Tiny Python Projects</a> site, and pick quick task to train your skills.</p><![CDATA[Installing Lutris on Debian from A to Z]]>https://sawczuk.dev/posts/installing-lutris-on-debian-from-a-to-zhttps://sawczuk.dev/posts/installing-lutris-on-debian-from-a-to-zSat, 16 Oct 2021 08:32:54 GMT<p>After Nth attempt I finally managed to install fully working Lustris on Debian. There were some ups and downs during the installation, but finally I was able to get a fully working Wine with VulcanAPI support.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/b0d0cc4749de88332925ad0e9fcaaabc/47311/server-9.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMEAv/EABYBAQEBAAAAAAAAAAAAAAAAAAIBA//aAAwDAQACEAMQAAABkS9LyyWEX//EABwQAAEDBQAAAAAAAAAAAAAAAAABETICBBMiQv/aAAgBAQABBQJtkMblE+reH//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABwQAAICAgMAAAAAAAAAAAAAAAABAhExYXKBgv/aAAgBAQAGPwKlgktFxFxZ4Oz/xAAdEAEAAQQDAQAAAAAAAAAAAAABABEhMUEQUXHw/9oACAEBAAE/IXB3LV7CE2jfKyAIm+MH15Msf//aAAwDAQACAAMAAAAQ1+//xAAWEQEBAQAAAAAAAAAAAAAAAAAAARH/2gAIAQMBAT8QjX//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAgEBPxBH/8QAHxABAAIBAwUAAAAAAAAAAAAAAQARITGx8FGBkaHB/9oACAEBAAE/ECEFqAyF3qCSXArRaZ8Isc9sMlEw7X3nKdGaPGue0+T/2Q=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/b0d0cc4749de88332925ad0e9fcaaabc/8ac56/server-9.webp 240w, /static/b0d0cc4749de88332925ad0e9fcaaabc/d3be9/server-9.webp 480w, /static/b0d0cc4749de88332925ad0e9fcaaabc/e46b2/server-9.webp 960w, /static/b0d0cc4749de88332925ad0e9fcaaabc/260c2/server-9.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/b0d0cc4749de88332925ad0e9fcaaabc/09b79/server-9.jpg 240w, /static/b0d0cc4749de88332925ad0e9fcaaabc/7cc5e/server-9.jpg 480w, /static/b0d0cc4749de88332925ad0e9fcaaabc/6a068/server-9.jpg 960w, /static/b0d0cc4749de88332925ad0e9fcaaabc/47311/server-9.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/b0d0cc4749de88332925ad0e9fcaaabc/6a068/server-9.jpg" alt="Installing Lutris on Debian from A to Z" title="Installing Lutris on Debian from A to Z" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>First, time to introduce my old but strong potato machine. It’s durable (hole in the wall confirm it) ThinkPad T450s. So… It’s components are not great and not terrible - i5-5300U with Intel HD 5500. And that added me additional problems to the list. As you can see, Vulcan have only <a href="https://software.intel.com/content/www/us/en/develop/blogs/new-intel-vulkan-beta-1540204404-graphics-driver-for-windows-78110-1540.html" target="_blank" rel="nofollow noopener noreferrer">partial support</a> for older CPU Intel generations, so it could cause problems if you are not aware off different installation of dxvk library. For basic installation of dxvk on Debian with only Intel graphics you can follow <a href="https://github.com/doitsujin/dxvk/releases" target="_blank" rel="nofollow noopener noreferrer">mesa driver guide</a> that is required also <a href="https://github.com/lutris/docs/blob/master/InstallingDrivers.md" target="_blank" rel="nofollow noopener noreferrer">by the Lutris</a>.</p> <p>When installing Lutris, be aware, that you need ALL required components of Wine! Even when Lustris manage to download its own Wine libs, it’s still using some system libraries. That’s why you should follow fully <a href="https://github.com/lutris/docs/blob/master/WineDependencies.md" target="_blank" rel="nofollow noopener noreferrer">Wine Dependencies guide</a>. </p> <p>As I mention before, I had problem with Debian installation of Wine on my laptop. Main problem was caused by wrongly committed version to debian repository - and it’s common problem for past few months in Wine community…</p> <div class="gatsby-highlight" data-language="shell"><pre class="language-shell"><code class="language-shell"><span class="token function">sudo</span> <span class="token function">apt-get</span> <span class="token function">install</span> --install-recommends winehq-staging The following packages have unmet dependencies: winehq-staging <span class="token builtin class-name">:</span> Depends: wine-staging <span class="token punctuation">(</span><span class="token operator">=</span> <span class="token number">6.17</span>~focal-1<span class="token punctuation">)</span> E: Unable to correct problems, you have held broken packages.</code></pre></div> <p>To ‘fix’ it, just enter to download site of Wine, and download and install <a href="https://dl.winehq.org/wine-builds/debian/dists/buster/main/binary-amd64/" target="_blank" rel="nofollow noopener noreferrer">winehq-staging .deb</a> package manually. Causing problem could occur by missing libfaudio0 library, which could not be installed with package manager… But thanks to <a href="https://askubuntu.com/questions/1145473/how-do-i-install-libfaudio0" target="_blank" rel="nofollow noopener noreferrer">this post</a> on Ubuntu forum, I found <a href="https://download.opensuse.org/repositories/Emulators:/Wine:/Debian/Debian_10/amd64/" target="_blank" rel="nofollow noopener noreferrer">open repository</a> containing right library package.</p> <p>After this, I finally could play my games running on DirectX 9 (But translated in air by Vulcan, which give additional 50% boost). So yea, game was pretty much playable with stable 20 fps (and with later expansion 17fps), on the lowest settings - but I didn’t expect much, and I was glad that it could ever work on this device. But I have one conclusion - If you want a Linux distro for gaming proposes, you should install Ubuntu or Pop!OS… It’s easier to set up.</p><![CDATA[Fixing my e-bike's speedometer and battery]]>https://sawczuk.dev/posts/fixing-my-e-bikes-speedometer-and-batteryhttps://sawczuk.dev/posts/fixing-my-e-bikes-speedometer-and-batterySun, 26 Sep 2021 21:47:27 GMT<p>In a word of introduction, I am the owner of an e-bike with a Bosch motor - as far as I can tell, it is the second version, powered by BatteryPack Classic 400 battery. I use the bike every day, and it allows me to get to many distant places in the city without much fatigue. Unfortunately, serving electronics for this type of equipment is a problem, and we are often left alone with repairing it. And in recent days one of the components failed.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/edc34f018a6b7e4cb62fd0ce2dac7bec/47311/server-7.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMFBP/EABUBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAGRpnNKSAz/AP/EABkQAQEAAwEAAAAAAAAAAAAAAAEAAgMTQf/aAAgBAQABBQLU4i80i8v/xAAWEQADAAAAAAAAAAAAAAAAAAABAhD/2gAIAQMBAT8BUz//xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAgEBPwFn/8QAGhAAAQUBAAAAAAAAAAAAAAAAAQACECGBEf/aAAgBAQAGPwK+4jeFsmP/xAAcEAACAQUBAAAAAAAAAAAAAAAAATEQEVGRsfH/2gAIAQEAAT8hc3vWGYY+kckjlT//2gAMAwEAAgADAAAAEDcP/8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQMBAT8QUEn/xAAWEQEBAQAAAAAAAAAAAAAAAAABERD/2gAIAQIBAT8QFjn/xAAbEAEAAQUBAAAAAAAAAAAAAAABEQAQITFxQf/aAAgBAQABPxAwJxhOHRM0r2vhq8GuzUJRrywWn//Z'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/edc34f018a6b7e4cb62fd0ce2dac7bec/8ac56/server-7.webp 240w, /static/edc34f018a6b7e4cb62fd0ce2dac7bec/d3be9/server-7.webp 480w, /static/edc34f018a6b7e4cb62fd0ce2dac7bec/e46b2/server-7.webp 960w, /static/edc34f018a6b7e4cb62fd0ce2dac7bec/260c2/server-7.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/edc34f018a6b7e4cb62fd0ce2dac7bec/09b79/server-7.jpg 240w, /static/edc34f018a6b7e4cb62fd0ce2dac7bec/7cc5e/server-7.jpg 480w, /static/edc34f018a6b7e4cb62fd0ce2dac7bec/6a068/server-7.jpg 960w, /static/edc34f018a6b7e4cb62fd0ce2dac7bec/47311/server-7.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/edc34f018a6b7e4cb62fd0ce2dac7bec/6a068/server-7.jpg" alt="Fixing my e-bike&#39;s speedometer and battery" title="Fixing my e-bike&#39;s speedometer and battery" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>But how did it happen? During the last transport of the bike I saw that the sensor of speedometer couldn’t send data to my control unit, and because of this, the motor on my bike stopped working. As it turns out, the magnet that is read by the sensor should be at the height of the line marked on the sensor itself. After a day of searching, I found this information on one of the <a href="https://www.youtube.com/watch?v=6RJLq0E1OhE" target="_blank" rel="nofollow noopener noreferrer">YT videos</a></p> <p>Unfortunately, correcting the magnet did not solve the problem. The second point was that my battery in the Bosch BatteryPack Classic 400 suddenly shut down, and I could not read the status of the current draw from the LED lights, and the charger would not allow me to recharge the battery. It took me a long time to find the cause, but I found a <a href="https://bicycles.stackexchange.com/questions/52465/bosch-battery-stopped-charging" target="_blank" rel="nofollow noopener noreferrer">link on one of the bike forums</a> of a person with a similar problem. One of the causes of a faulty battery could be that it is protected from shorting out or receiving too high/low a temperature. When battery gets stuck and can’t show charging status, just hold status button on it for 30s to give this component a soft reset. This way we can get the battery back in working order. </p> <p>Given method fixed my bike, and now I can power up by control unit + read speed, so I can continue to enjoy riding. I wanted to write this note here because it was hard for me to find information how to fix battery problem. I don’t wish anyone to have this problem, but I will be glad if one day this post will help someone to solve this with his own equipment.</p> <p>Check also this site to solve <a href="https://ebikeshq.com/common-ebike-battery-problems/" target="_blank" rel="nofollow noopener noreferrer">most common battery problems</a></p><![CDATA[Format the code automatically before commit]]>https://sawczuk.dev/posts/format-the-code-automatically-before-commithttps://sawczuk.dev/posts/format-the-code-automatically-before-commitFri, 24 Sep 2021 14:56:53 GMT<p>There is a situation where we forget to format our code to one standard before submitting changes. But what if git does it for us?</p> <p>There are a couple of solutions, like plugging directly into git hooks commands, or using a framework like <a href="https://pre-commit.com/" target="_blank" rel="nofollow noopener noreferrer">https://pre-commit.com/</a> which will visually help us check out the project, and add code checking plugins for us.</p> <div class="gatsby-highlight" data-language="shell"><pre class="language-shell"><code class="language-shell"><span class="token comment"># Format code with terraform &amp; terragrunt standard</span> terragrunt hclfmt terraform <span class="token function">fmt</span> --recursive <span class="token comment"># Add missing new-line in files</span> <span class="token function">git</span> ls-files -z <span class="token operator">|</span> <span class="token keyword">while</span> <span class="token assign-left variable"><span class="token environment constant">IFS</span></span><span class="token operator">=</span> <span class="token builtin class-name">read</span> -rd <span class="token string">''</span> f<span class="token punctuation">;</span> <span class="token keyword">do</span> <span class="token function">tail</span> -c1 <span class="token operator">&lt;</span> <span class="token string">"<span class="token variable">$f</span>"</span> <span class="token operator">|</span> <span class="token builtin class-name">read</span> -r _ <span class="token operator">||</span> <span class="token builtin class-name">echo</span> <span class="token operator">>></span> <span class="token string">"<span class="token variable">$f</span>"</span><span class="token punctuation">;</span> <span class="token keyword">done</span></code></pre></div> <p>It’s especially useful when we need to run a lot of commands responsible for testing, linter code and checking integration</p><![CDATA[Sync AWS CodeCommit with Bitbucket by running pipeline]]>https://sawczuk.dev/posts/sync-aws-codecommit-with-bitbucket-by-running-pipelinehttps://sawczuk.dev/posts/sync-aws-codecommit-with-bitbucket-by-running-pipelineSun, 01 Aug 2021 23:01:48 GMT<p>If you don’t have access to the official integration of the two apps and their repositories, just do your own sync.</p> <h3 id="set-ssh" style="position:relative;"><a href="#set-ssh" aria-label="set ssh permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Set SSH</h3> <p>To prepare your repositories, enable pipelines in your main repository. Then in repository options let Bitbucket create new ssh key, that will be stored in <code class="language-text">/opt/atlassian/pipelines/agent/ssh/id_rsa</code> path. Try to add generated public key to IAM user AWS CodeCommit keys in Security credentials tab in AWS Console </p> <h3 id="create-config-file" style="position:relative;"><a href="#create-config-file" aria-label="create config file permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Create config file</h3> <p>Create new config that will be later used in our pipeline with: <code class="language-text">nano ~/.ssh/configBitbucket</code></p> <div class="gatsby-highlight" data-language="config"><pre class="language-config"><code class="language-config">Host git-codecommit.*.amazonaws.com Hostname git-codecommit.eu-central-1.amazonaws.com User YOUR_USER_SSH_KEY IdentityFile /opt/atlassian/pipelines/agent/ssh/id_rsa</code></pre></div> <p>Later try to encode it with: <code class="language-text">base64 ~/.ssh/configBitbucket</code></p> <h3 id="set-environment-variables" style="position:relative;"><a href="#set-environment-variables" aria-label="set environment variables permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Set Environment Variables</h3> <p>Now with base64 result, use it in Bitbucket Environment Variables</p> <ul> <li>CodeCommitConfig: OUR<em>BASE64</em>CONFIG</li> <li>CodeCommitHost: git-codecommit.aws-region.amazonaws.com</li> <li>CodeCommitRepository: git-codecommit.aws-region.amazonaws.com/v1/repos/sample</li> <li>CodeCommitUser: SSH<em>USER</em>PUBLIC_KEY</li> </ul> <h3 id="create-pipeline-step" style="position:relative;"><a href="#create-pipeline-step" aria-label="create pipeline step permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Create Pipeline Step</h3> <p>Provided env’s will be used in pipeline step below</p> <div class="gatsby-highlight" data-language="yaml"><pre class="language-yaml"><code class="language-yaml"><span class="token key atrule">definitions</span><span class="token punctuation">:</span> <span class="token key atrule">steps</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">step</span><span class="token punctuation">:</span> <span class="token important">&amp;sync-codecommit</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> Sync changes to CodeCommit <span class="token key atrule">script</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> echo $CodeCommitConfig <span class="token punctuation">></span> ~/.ssh/config.tmp <span class="token punctuation">-</span> base64 <span class="token punctuation">-</span>d ~/.ssh/config.tmp <span class="token punctuation">></span> ~/.ssh/config <span class="token punctuation">-</span> cat ~/.ssh/config <span class="token punctuation">-</span> set +e <span class="token punctuation">-</span> ssh <span class="token punctuation">-</span>o StrictHostKeyChecking=no $CodeCommitHost <span class="token punctuation">-</span> set <span class="token punctuation">-</span>e <span class="token punctuation">-</span> git remote add codecommit ssh<span class="token punctuation">:</span>//$CodeCommitRepository <span class="token punctuation">-</span> git push codecommit $BITBUCKET_BRANCH <span class="token punctuation">-</span><span class="token punctuation">-</span>force <span class="token key atrule">pipelines</span><span class="token punctuation">:</span> <span class="token key atrule">default</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">step</span><span class="token punctuation">:</span> <span class="token important">*sync-codecommit</span></code></pre></div> <p>Now, whenever you push to Bitbucket, it will also push to CodeCommit. For more details and better step-by-step configuration you can check also this <a href="https://medium.com/@wooltar/aws-codecommit-push-from-bitbucket-pipeline-2f5e08fe3629" target="_blank" rel="nofollow noopener noreferrer">Medium post</a></p><![CDATA[Set access to k8s thru RBAC Token]]>https://sawczuk.dev/posts/set-access-to-k8s-thru-rbac-tokenhttps://sawczuk.dev/posts/set-access-to-k8s-thru-rbac-tokenSun, 01 Aug 2021 20:28:38 GMT<p>Recently, I was trying to find a suitable way to share the k8s logs with the development team. After searching the internet, I finally found a suitable and current solution for the new kuberentes versions. With a little help and a mini-customization of the code, I present the result for everyone who will need help. Let the code be with you!</p> <p>(Just watch out for TLS auth… Soo… To be fixed later… ) (ó﹏ò。)</p> <div class="gatsby-highlight" data-language="shell"><pre class="language-shell"><code class="language-shell"><span class="token shebang important">#!/bin/bash</span> <span class="token keyword">if</span> <span class="token punctuation">[</span> -z <span class="token variable">${CLUSTERNAME}</span> <span class="token punctuation">]</span> <span class="token operator">||</span> <span class="token punctuation">[</span> -z <span class="token variable">${NAMESPACE}</span> <span class="token punctuation">]</span> <span class="token operator">||</span> <span class="token punctuation">[</span> -z <span class="token variable">${USERNAME}</span> <span class="token punctuation">]</span> <span class="token operator">||</span> <span class="token punctuation">[</span> -z <span class="token variable">${K8S_CONTEXT}</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span> <span class="token builtin class-name">echo</span> Usage: <span class="token assign-left variable">K8S_CONTEXT</span><span class="token operator">=</span>admin.context <span class="token assign-left variable">CLUSTERNAME</span><span class="token operator">=</span>project.name <span class="token assign-left variable">NAMESPACE</span><span class="token operator">=</span>default <span class="token assign-left variable">USERNAME</span><span class="token operator">=</span>dev ./script.sh <span class="token operator">&amp;&amp;</span> <span class="token builtin class-name">exit</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token keyword">fi</span><span class="token punctuation">;</span> <span class="token comment"># Command with context to admin user</span> <span class="token assign-left variable">KUBE_COMMAND</span><span class="token operator">=</span><span class="token string">"kubectl --context <span class="token variable">$K8S_CONTEXT</span>"</span> <span class="token variable">$KUBE_COMMAND</span> delete serviceaccount/<span class="token variable">$USERNAME</span>-sa <span class="token variable">$KUBE_COMMAND</span> delete clusterrole.rbac.authorization.k8s.io/<span class="token variable">$USERNAME</span>-cr <span class="token variable">$KUBE_COMMAND</span> delete rolebinding.rbac.authorization.k8s.io/<span class="token variable">$USERNAME</span>-rb <span class="token function">cat</span> <span class="token operator">&lt;&lt;</span><span class="token string">EOF<span class="token bash punctuation"> <span class="token operator">|</span> <span class="token variable">$KUBE_COMMAND</span> apply -f -</span> apiVersion: v1 kind: ServiceAccount metadata: name: <span class="token variable">$USERNAME</span>-sa namespace: <span class="token variable">$NAMESPACE</span> EOF</span> <span class="token comment"># Set here what resources are allowed to enter</span> <span class="token function">cat</span> <span class="token operator">&lt;&lt;</span><span class="token string">EOF<span class="token bash punctuation"> <span class="token operator">|</span> <span class="token variable">$KUBE_COMMAND</span> apply -f -</span> apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: <span class="token variable">$USERNAME</span>-cr rules: - verbs: ["get", "list", "watch"] resources: - pods - deployments - jobs - cronjobs - pods/log apiGroups: ["", "apps", "batch"] EOF</span> <span class="token function">cat</span> <span class="token operator">&lt;&lt;</span><span class="token string">EOF<span class="token bash punctuation"> <span class="token operator">|</span> <span class="token variable">$KUBE_COMMAND</span> apply -f -</span> apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: <span class="token variable">$USERNAME</span>-rb namespace: <span class="token variable">$NAMESPACE</span> subjects: - kind: ServiceAccount name: <span class="token variable">$USERNAME</span>-sa namespace: <span class="token variable">$NAMESPACE</span> roleRef: kind: ClusterRole name: <span class="token variable">$USERNAME</span>-cr apiGroup: rbac.authorization.k8s.io EOF</span> <span class="token assign-left variable">TOKEN</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span>$KUBE_COMMAND describe -n default secrets <span class="token string">"<span class="token variable"><span class="token variable">$(</span>$KUBE_COMMAND describe -n default serviceaccount $USERNAME-sa <span class="token operator">|</span> <span class="token function">grep</span> -i Tokens <span class="token operator">|</span> <span class="token function">awk</span> <span class="token string">'{print <span class="token variable">$2</span>}'</span><span class="token variable">)</span></span>"</span> <span class="token operator">|</span> <span class="token function">grep</span> token: <span class="token operator">|</span> <span class="token function">awk</span> <span class="token string">'{print <span class="token variable">$2</span>}'</span><span class="token variable">)</span></span> kubectl --kubeconfig<span class="token operator">=</span>kubeconfig-<span class="token variable">$CLUSTERNAME</span> config set-cluster <span class="token variable">$CLUSTERNAME</span> --server<span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span>$KUBE_COMMAND config view -o <span class="token assign-left variable">jsonpath</span><span class="token operator">=</span><span class="token string">'{.clusters[0].cluster.server}'</span><span class="token variable">)</span></span> kubectl --kubeconfig<span class="token operator">=</span>kubeconfig-<span class="token variable">$CLUSTERNAME</span> config set-credentials <span class="token variable">$CLUSTERNAME</span>-<span class="token variable">$USERNAME</span> --token<span class="token operator">=</span><span class="token variable">$TOKEN</span> --kubeconfig<span class="token operator">=</span>kubeconfig-<span class="token variable">$CLUSTERNAME</span> kubectl --kubeconfig<span class="token operator">=</span>kubeconfig-<span class="token variable">$CLUSTERNAME</span> config set-context <span class="token variable">$CLUSTERNAME</span> --cluster<span class="token operator">=</span><span class="token variable">$CLUSTERNAME</span> --user<span class="token operator">=</span><span class="token variable">$CLUSTERNAME</span>-<span class="token variable">$USERNAME</span> kubectl --kubeconfig<span class="token operator">=</span>kubeconfig-<span class="token variable">$CLUSTERNAME</span> config use-context <span class="token variable">$CLUSTERNAME</span> kubectl --kubeconfig<span class="token operator">=</span>kubeconfig-<span class="token variable">$CLUSTERNAME</span> --insecure-skip-tls-verify auth can-i get pods --namespace <span class="token variable">$NAMESPACE</span></code></pre></div><![CDATA[GitFlow & Versioning]]>https://sawczuk.dev/posts/gitflow-and-versioninghttps://sawczuk.dev/posts/gitflow-and-versioningThu, 01 Jul 2021 20:41:32 GMT<p>Trying to keep the branch in order, we can use the GitFlow formula and adjust our tactics for future product releases. Each version and its important aspect has its own initials and ever additional values as release candidates. In addition, the tagging system and its automation ensure the integrity of project tags even in the event of unwanted but often used hotfixes of the main branch.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 654px; " > <a class="gatsby-resp-image-link" href="/static/999f62e5fb31ed44530921ff779c328b/68e9c/gitflow-branches.png" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 20.416666666666668%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsSAAALEgHS3X78AAAAyElEQVQY0zWQ2w6CMAyGx8ZpA0aQUzgJylEFI8QLvdT3fyhbM5p86Z9229+OEBVB+eaYjWvTi3KMUJvTeZHH1QNJzaldwvZjYZ2dso5Pc45asy0dkgQywCNM51gI3GCcKbVKM8oLL543rPHsUslk7UH7Ih1uQjYP1NRxayccXuqRBkiAFL3QpAJqnAK4KzcbeB4IcTVCYBKtU4uUoAfIOIQANnUft4v3bbk6gOESygylzW+SdJP0o73PdMGU2R6Oxv4t/IoCh/kBlpwMJryZQnoAAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/999f62e5fb31ed44530921ff779c328b/8ac56/gitflow-branches.webp 240w, /static/999f62e5fb31ed44530921ff779c328b/d3be9/gitflow-branches.webp 480w, /static/999f62e5fb31ed44530921ff779c328b/d7085/gitflow-branches.webp 654w" sizes="(max-width: 654px) 100vw, 654px" type="image/webp" /> <source srcset="/static/999f62e5fb31ed44530921ff779c328b/8ff5a/gitflow-branches.png 240w, /static/999f62e5fb31ed44530921ff779c328b/e85cb/gitflow-branches.png 480w, /static/999f62e5fb31ed44530921ff779c328b/68e9c/gitflow-branches.png 654w" sizes="(max-width: 654px) 100vw, 654px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/999f62e5fb31ed44530921ff779c328b/68e9c/gitflow-branches.png" alt="Merge strategies" title="Merge strategies" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>The defined version is especially needed when releasing the application issuing the API because specifying this source can tell us about possible fixed things, new functionalities, or a new version incompatible with the previous one.</p> <p>To be sure what version our code change should be, we can keep an additional changelog <a href="https://keepachangelog.com/en/1.0.0/" target="_blank" rel="nofollow noopener noreferrer">CHANGELOG.md</a> which will help people find out what things are currently in the branch to be released and what things have been changed/fixed/added in recent commits</p> <ul> <li><a href="https://semver.org/spec/v2.0.0.html" target="_blank" rel="nofollow noopener noreferrer">Semantic Versioning 2.0.0</a></li> <li><a href="https://keepachangelog.com/en/1.0.0/" target="_blank" rel="nofollow noopener noreferrer">Keep a Changelog</a></li> </ul> <h2 id="tags-automation" style="position:relative;"><a href="#tags-automation" aria-label="tags automation permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Tags Automation</h2> <p>The script below can be a nice help as it downloads the latest tags and then adds new ones as requested by the user. It is suggested to create and post tags on the target branch</p> <div class="gatsby-highlight" data-language="makefile"><pre class="language-makefile"><code class="language-makefile"><span class="token comment">#--------------------------------- Git ----------------------------------------------------#</span> GIT_LAST_TAG <span class="token operator">=</span> <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">lastword</span> <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">shell</span> git tag --sort<span class="token operator">=</span>taggerdate<span class="token punctuation">)</span><span class="token punctuation">)</span> GIT_VERSION <span class="token operator">:=</span> <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">subst</span> -RC, , <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">subst</span> v,, <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">subst</span> ., ,<span class="token variable">$</span><span class="token punctuation">(</span>GIT_LAST_TAG<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> GIT_MAJOR <span class="token operator">:=</span> <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">word</span> 1, <span class="token variable">$</span><span class="token punctuation">(</span>GIT_VERSION<span class="token punctuation">)</span><span class="token punctuation">)</span> GIT_MINOR <span class="token operator">:=</span> <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">word</span> 2, <span class="token variable">$</span><span class="token punctuation">(</span>GIT_VERSION<span class="token punctuation">)</span><span class="token punctuation">)</span> GIT_PATCH <span class="token operator">:=</span> <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">word</span> 3, <span class="token variable">$</span><span class="token punctuation">(</span>GIT_VERSION<span class="token punctuation">)</span><span class="token punctuation">)</span> GIT_RC <span class="token operator">:=</span> <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">word</span> 4, <span class="token variable">$</span><span class="token punctuation">(</span>GIT_VERSION<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">#--------------------------------- Git ----------------------------------------------------#</span> <span class="token symbol">tag-major</span><span class="token punctuation">:</span> <span class="token keyword">ifeq</span> <span class="token punctuation">(</span><span class="token variable">$</span><span class="token punctuation">(</span>GIT_RC<span class="token punctuation">)</span>,<span class="token punctuation">)</span> <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">eval</span> tag <span class="token operator">=</span> v<span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">shell</span> echo <span class="token variable">$</span><span class="token punctuation">(</span>GIT_MAJOR<span class="token punctuation">)</span> + 1 <span class="token operator">|</span> bc<span class="token punctuation">)</span>.0.0<span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">eval</span> tag <span class="token operator">=</span> v<span class="token variable">$</span><span class="token punctuation">(</span>GIT_MAJOR<span class="token punctuation">)</span>.0.0<span class="token punctuation">)</span> <span class="token keyword">endif</span> git tag <span class="token variable">$</span><span class="token punctuation">(</span>tag<span class="token punctuation">)</span> -m <span class="token variable">$</span><span class="token punctuation">(</span>tag<span class="token punctuation">)</span> <span class="token symbol">tag-major-rc</span><span class="token punctuation">:</span> <span class="token keyword">ifeq</span> <span class="token punctuation">(</span><span class="token variable">$</span><span class="token punctuation">(</span>GIT_RC<span class="token punctuation">)</span>,<span class="token punctuation">)</span> <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">eval</span> tag <span class="token operator">=</span> v<span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">shell</span> echo <span class="token variable">$</span><span class="token punctuation">(</span>GIT_MAJOR<span class="token punctuation">)</span> + 1 <span class="token operator">|</span> bc<span class="token punctuation">)</span>.0.0-RC1<span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">eval</span> tag <span class="token operator">=</span> v<span class="token variable">$</span><span class="token punctuation">(</span>GIT_MAJOR<span class="token punctuation">)</span>.0.0-RC<span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">shell</span> echo <span class="token variable">$</span><span class="token punctuation">(</span>GIT_RC<span class="token punctuation">)</span> + 1 <span class="token operator">|</span> bc<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">endif</span> git tag <span class="token variable">$</span><span class="token punctuation">(</span>tag<span class="token punctuation">)</span> -m <span class="token variable">$</span><span class="token punctuation">(</span>tag<span class="token punctuation">)</span> <span class="token symbol">tag-minor</span><span class="token punctuation">:</span> <span class="token keyword">ifeq</span> <span class="token punctuation">(</span><span class="token variable">$</span><span class="token punctuation">(</span>GIT_RC<span class="token punctuation">)</span>,<span class="token punctuation">)</span> <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">eval</span> tag <span class="token operator">=</span> v<span class="token variable">$</span><span class="token punctuation">(</span>GIT_MAJOR<span class="token punctuation">)</span>.<span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">shell</span> echo <span class="token variable">$</span><span class="token punctuation">(</span>GIT_MINOR<span class="token punctuation">)</span> + 1 <span class="token operator">|</span> bc<span class="token punctuation">)</span>.0<span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">eval</span> tag <span class="token operator">=</span> v<span class="token variable">$</span><span class="token punctuation">(</span>GIT_MAJOR<span class="token punctuation">)</span>.<span class="token variable">$</span><span class="token punctuation">(</span>GIT_MINOR<span class="token punctuation">)</span>.0<span class="token punctuation">)</span> <span class="token keyword">endif</span> git tag <span class="token variable">$</span><span class="token punctuation">(</span>tag<span class="token punctuation">)</span> -m <span class="token variable">$</span><span class="token punctuation">(</span>tag<span class="token punctuation">)</span> <span class="token symbol">tag-minor-rc</span><span class="token punctuation">:</span> <span class="token keyword">ifeq</span> <span class="token punctuation">(</span><span class="token variable">$</span><span class="token punctuation">(</span>GIT_RC<span class="token punctuation">)</span>,<span class="token punctuation">)</span> <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">eval</span> tag <span class="token operator">=</span> v<span class="token variable">$</span><span class="token punctuation">(</span>GIT_MAJOR<span class="token punctuation">)</span>.<span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">shell</span> echo <span class="token variable">$</span><span class="token punctuation">(</span>GIT_MINOR<span class="token punctuation">)</span> + 1 <span class="token operator">|</span> bc<span class="token punctuation">)</span>.0-RC1<span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">eval</span> tag <span class="token operator">=</span> v<span class="token variable">$</span><span class="token punctuation">(</span>GIT_MAJOR<span class="token punctuation">)</span>.<span class="token variable">$</span><span class="token punctuation">(</span>GIT_MINOR<span class="token punctuation">)</span>.0-RC<span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">shell</span> echo <span class="token variable">$</span><span class="token punctuation">(</span>GIT_RC<span class="token punctuation">)</span> + 1 <span class="token operator">|</span> bc<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">endif</span> git tag <span class="token variable">$</span><span class="token punctuation">(</span>tag<span class="token punctuation">)</span> -m <span class="token variable">$</span><span class="token punctuation">(</span>tag<span class="token punctuation">)</span> <span class="token symbol">tag-patch</span><span class="token punctuation">:</span> <span class="token keyword">ifeq</span> <span class="token punctuation">(</span><span class="token variable">$</span><span class="token punctuation">(</span>GIT_RC<span class="token punctuation">)</span>,<span class="token punctuation">)</span> <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">eval</span> tag <span class="token operator">=</span> v<span class="token variable">$</span><span class="token punctuation">(</span>GIT_MAJOR<span class="token punctuation">)</span>.<span class="token variable">$</span><span class="token punctuation">(</span>GIT_MINOR<span class="token punctuation">)</span>.<span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">shell</span> echo <span class="token variable">$</span><span class="token punctuation">(</span>GIT_PATCH<span class="token punctuation">)</span> + 1 <span class="token operator">|</span> bc<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">eval</span> tag <span class="token operator">=</span> v<span class="token variable">$</span><span class="token punctuation">(</span>GIT_MAJOR<span class="token punctuation">)</span>.<span class="token variable">$</span><span class="token punctuation">(</span>GIT_MINOR<span class="token punctuation">)</span>.<span class="token variable">$</span><span class="token punctuation">(</span>GIT_PATCH<span class="token punctuation">)</span><span class="token punctuation">)</span> endifgit tag <span class="token variable">$</span><span class="token punctuation">(</span>tag<span class="token punctuation">)</span> -m <span class="token variable">$</span><span class="token punctuation">(</span>tag<span class="token punctuation">)</span> <span class="token symbol">tag-patch-rc</span><span class="token punctuation">:</span> <span class="token keyword">ifeq</span> <span class="token punctuation">(</span><span class="token variable">$</span><span class="token punctuation">(</span>GIT_RC<span class="token punctuation">)</span>,<span class="token punctuation">)</span> <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">eval</span> tag <span class="token operator">=</span> v<span class="token variable">$</span><span class="token punctuation">(</span>GIT_MAJOR<span class="token punctuation">)</span>.<span class="token variable">$</span><span class="token punctuation">(</span>GIT_MINOR<span class="token punctuation">)</span>.<span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">shell</span> echo <span class="token variable">$</span><span class="token punctuation">(</span>GIT_PATCH<span class="token punctuation">)</span> + 1 <span class="token operator">|</span> bc<span class="token punctuation">)</span>-RC1<span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">eval</span> tag <span class="token operator">=</span> v<span class="token variable">$</span><span class="token punctuation">(</span>GIT_MAJOR<span class="token punctuation">)</span>.<span class="token variable">$</span><span class="token punctuation">(</span>GIT_MINOR<span class="token punctuation">)</span>.<span class="token variable">$</span><span class="token punctuation">(</span>GIT_PATCH<span class="token punctuation">)</span>-RC<span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">shell</span> echo <span class="token variable">$</span><span class="token punctuation">(</span>GIT_RC<span class="token punctuation">)</span> + 1 <span class="token operator">|</span> bc<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">endif</span> git tag <span class="token variable">$</span><span class="token punctuation">(</span>tag<span class="token punctuation">)</span> -m <span class="token variable">$</span><span class="token punctuation">(</span>tag<span class="token punctuation">)</span> git tag <span class="token variable">$</span><span class="token punctuation">(</span>tag<span class="token punctuation">)</span> -m <span class="token variable">$</span><span class="token punctuation">(</span>tag<span class="token punctuation">)</span> <span class="token symbol">tag-patch-rc</span><span class="token punctuation">:</span> <span class="token keyword">ifeq</span> <span class="token punctuation">(</span><span class="token variable">$</span><span class="token punctuation">(</span>GIT_RC<span class="token punctuation">)</span>,<span class="token punctuation">)</span> <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">eval</span> tag <span class="token operator">=</span> v<span class="token variable">$</span><span class="token punctuation">(</span>GIT_MAJOR<span class="token punctuation">)</span>.<span class="token variable">$</span><span class="token punctuation">(</span>GIT_MINOR<span class="token punctuation">)</span>.<span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">shell</span> echo <span class="token variable">$</span><span class="token punctuation">(</span>GIT_PATCH<span class="token punctuation">)</span> + 1 <span class="token operator">|</span> bc<span class="token punctuation">)</span>-RC1<span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">eval</span> tag <span class="token operator">=</span> v<span class="token variable">$</span><span class="token punctuation">(</span>GIT_MAJOR<span class="token punctuation">)</span>.<span class="token variable">$</span><span class="token punctuation">(</span>GIT_MINOR<span class="token punctuation">)</span>.<span class="token variable">$</span><span class="token punctuation">(</span>GIT_PATCH<span class="token punctuation">)</span>-RC<span class="token variable">$</span><span class="token punctuation">(</span><span class="token keyword">shell</span> echo <span class="token variable">$</span><span class="token punctuation">(</span>GIT_RC<span class="token punctuation">)</span> + 1 <span class="token operator">|</span> bc<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">endif</span> git tag <span class="token variable">$</span><span class="token punctuation">(</span>tag<span class="token punctuation">)</span> -m <span class="token variable">$</span><span class="token punctuation">(</span>tag<span class="token punctuation">)</span></code></pre></div> <h2 id="docker-images-versioning" style="position:relative;"><a href="#docker-images-versioning" aria-label="docker images versioning permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Docker Images Versioning</h2> <p>When it comes to updating the version with Docker images, the best example is checking libraries that customers do not necessarily want to have in the latest version. One example is the <a href="https://hub.docker.com/_/python" target="_blank" rel="nofollow noopener noreferrer">python image</a>.</p> <p>When we look at the image side, we see a description of the shared, simplified, and individual tags. What’s the difference and what version is best to put in our Dockerfile?</p> <p>Let’s assume that our project is currently using the newest python version <code class="language-text">3.7.4</code>. If we want the latest updates from the code side, we have several options. Downloading <code class="language-text">latest</code>, <code class="language-text">3</code>, and <code class="language-text">3.8</code> tags. Depending on how often we will update the code, we want to protect ourselves against possible “code deprecation” that may occur in future versions. narrowing down the tag versions to download specific trees can prevent us from getting the latest changes in the library, but also protect us against possible old functionalities removed (and even against changing to the full version of the library after a few years)</p> <p>Therefore, it can be said that the safest version for the production environment is setting tags targeting patches (like <code class="language-text">python:3.8</code>), and the development version for the minor or even major version (like <code class="language-text">python:3</code> or ever <code class="language-text">python:latest</code>) - due to the possibility of testing even the expected changes in the used library, which can then be safely updated in production</p><![CDATA[Git Merging Strategies]]>https://sawczuk.dev/posts/git-merging-strategieshttps://sawczuk.dev/posts/git-merging-strategiesWed, 30 Jun 2021 18:23:03 GMT<p>Git is a great tool for managing your code version. It is also a tool where we can easily draw figures such as triangles, trapezoids, and other figures. How to find yourself on a railroad crossing? Try not to get lost at our train station?</p> <h2 id="simpler-branches-with-git-rebase" style="position:relative;"><a href="#simpler-branches-with-git-rebase" aria-label="simpler branches with git rebase permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Simpler branches with <code class="language-text">git rebase</code></h2> <p>When creating the code, we don’t pay attention to what our branch looks like. It is logical, after all, the code and changes are only ours, even if we have many commits - and you know, it’s better to have changes saved in the cloud than locally on the disk. On the other hand, at the time of merging, it causes some kind of discomfort for the committer and the code merger. In order not to bury ourselves in a thousand commits, before sending our final changes, we can use some useful commands that will improve the visibility of our branch The appearance of the tree after merging, depending on the method used</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 550px; " > <a class="gatsby-resp-image-link" href="/static/7704dc406511fce13c78314529654927/dd45a/merge-strategies.png" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 92.5%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAAAsSAAALEgHS3X78AAACwUlEQVQ4y3VUbXPSQBC+CyG8pIRCQoCEhEAIlIQSacGOjlKt4wu+1E6t1fHto6Pf/OCM4yd/gP5n3LvbQEC8mZ3b7Gb3nt179giBRSVZo1SucZ3KJL1LklIGvSBsGVK1boOSSf6pUCmbEzmyJL1UkCrZvXSQ0n98Foj2j9UZfmw2ui/6TA9mP7jNcM9ERP/1wHDOdG5z7pNiuUtaw/fipNY8AF9V6PfWCcu1Gz5IxHStNqZsr9p3uA8OinV7ztuh23fBH5Pu0Tfh819Omr3LJtd7Fxsg6yDRFnCK+xSknbb5mNDsLMZW/7rBKwlerSPtwduO6S1iUAvL5ZIgGh7cDC6naiUYrU6BC+lhW0zvyajhn9c5ou7zdUKz/dgEh8mqhxDey5zapKKsc2evMmA93JMVTbIH1ys0tfbDqN55yttR0kd0u7wM3D2DP9kqWQGRgSJ5ds6Wj1Xl77h9GgCPVKTICUgeHbBLB1RSGCkdkF7KVwEAQxCDoWckYAcnGUPkGqPAAsRFOyP0iKMX+zFIEX0GHsKAnIIM1hVAqYCQJWQlz1IlUfC14CIU/LmcKgts9Bh7TrE1Gz2UIdhDmqT7JItkVEekaZ+HbdpccMtWvfPMFnyjIW+ealFxk4/cnNpwU6WSaP4nIXarNfygpwcBx+vKKZuT2SqISsCxBXL0zQmMWyTG65T7vPhrcthhw79wkV4bIG283V2TcgTSSmzZ3D4g/C2ABFchJGcPBGmPP68jNWNslfSIE7qoeTQDDCrpIU9YMkaxZhw2Eh8Smsft16ee4T6oCmKH64Re/MWyD96FaXiDW7/ESxR+CmFeNxof3PxJ8EEI/en3+kZdUiaXR2KqkpxXkLiUj62ULeC3BuTOI+cUfHgLGFeC/4roUzlyRhfgGivLRILmkSIOjBz0j/p8MoQvQeRAHPSedpC/rM/uX/vIQtZhAgX7AAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/7704dc406511fce13c78314529654927/8ac56/merge-strategies.webp 240w, /static/7704dc406511fce13c78314529654927/d3be9/merge-strategies.webp 480w, /static/7704dc406511fce13c78314529654927/12b65/merge-strategies.webp 550w" sizes="(max-width: 550px) 100vw, 550px" type="image/webp" /> <source srcset="/static/7704dc406511fce13c78314529654927/8ff5a/merge-strategies.png 240w, /static/7704dc406511fce13c78314529654927/e85cb/merge-strategies.png 480w, /static/7704dc406511fce13c78314529654927/dd45a/merge-strategies.png 550w" sizes="(max-width: 550px) 100vw, 550px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/7704dc406511fce13c78314529654927/dd45a/merge-strategies.png" alt="Merge strategies" title="Merge strategies" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>The most classic method of combining changes is classic merge. Executing a simple command is an ideal option for projects carried out by one-person teams. However, it has one problem, when detecting conflicts between two branches, it forces checking the data on both sides for each commit that the user committed and comparing the changes since disconnected from the parent branch.</p> <div class="gatsby-highlight" data-language="shell"><pre class="language-shell"><code class="language-shell"><span class="token function">git</span> checkout master <span class="token function">git</span> merge topic</code></pre></div> <p>To limit the number of visible changes after merging branches, you can use the squash method when merging branches. It causes that all the commits that we have done so far on the branch topic will be rewritten and merged as one new commit. this means that the displayed history of our changes will have only one summary commit in the tree.</p> <div class="gatsby-highlight" data-language="shell"><pre class="language-shell"><code class="language-shell"><span class="token function">git</span> checkout master <span class="token function">git</span> merge topic --squash</code></pre></div> <p>The last operation requires slightly more effort, but its result in the change tree looks the clearest. Especially in repositories with a lot of changes and more people working on it. This method is to overwrite the commit and the entire branch topic before submitting the changes for merge request/pull request/code review. First of all, in this method, we are sure that the uploaded changes are the latest from the branch we use as a parent. Secondly, it gives us control over the content of the commits by enabling them to be edited and arranged appropriately. The third argument is a very short tree branch that we will create after merging. At best, this branch will create a clear triangle of changes - that is, reading the changes from the parent’s latest commit, uploading our changes, and confirming the branch connection. Building such a branch is much clearer than scrolling through the entire page of a trapezoidal merged branch changes <em>(changes that started, for example, 18 commits ago, but adding only one line of code in today’s commit)</em>. In addition to readability, the committing person is also sure that the changes he/she uploaded are up-to-date and will not have conflicts with the parent branch, because we already have the latest changes.</p> <div class="gatsby-highlight" data-language="shell"><pre class="language-shell"><code class="language-shell"><span class="token function">git</span> checkout topic <span class="token function">git</span> rebase --interactive HEAD~X <span class="token comment"># X is the number of commits that we want to rewrite (to be squashed or message edited)</span> <span class="token comment"># more about rebase and its use-cases https://git-scm.com/docs/git-rebase#_interactive_mode</span> <span class="token function">git</span> fetch origin/master <span class="token comment"># get lastest master commit without checking out to it</span> <span class="token function">git</span> rebase origin/master <span class="token comment"># rebase (rewrite) all commits on a branch to a new place above the lastest master changes</span> <span class="token function">git</span> push topic --force <span class="token comment"># rewritten branch must be pushed with force because it has whole new history and parent ID</span></code></pre></div><![CDATA[Easy and fast file sharing from the command-line]]>https://sawczuk.dev/posts/easy-and-fast-file-sharing-from-the-command-linehttps://sawczuk.dev/posts/easy-and-fast-file-sharing-from-the-command-lineSat, 17 Apr 2021 23:17:32 GMT<p>Looking for a quick and easy-to-use option to share and send files, I came across transfer.sh. As it turns out, this is an ideal form of file sharing like WeTransfer or mega.nz, but for sending files via CLI. Obviously, the best option for sending files is direct data transfer using SCP or rsync. But for Polish people that we can go public or servers that don’t have direct IP or exit, this is an interesting option.</p> <p>When it comes to sending files, curl is enough for us</p> <div class="gatsby-highlight" data-language="shell"><pre class="language-shell"><code class="language-shell"><span class="token comment"># Upload using cURL </span> $ <span class="token function">curl</span> --upload-file ./hello.txt https://transfer.sh/hello.txt https://transfer.sh/66nb8/hello.txt</code></pre></div> <p>On the website <a href="transfer.sh">https://transfer.sh/</a> we can see many options for using the tool. We have automatic packing of files to tar.gz or zip archives, examples of data encryption before uploading, malware scanning, use of <a href="keybase.io">https://keybase.io/</a>, and much more. The project even has a website at <a href="a%20Tor%20network">http://jxm5d6emw5rknovg.onion/</a></p><![CDATA[Not knowing Docker image configuration? Try runalike]]>https://sawczuk.dev/posts/not-knowing-docker-image-configuration-try-runalikehttps://sawczuk.dev/posts/not-knowing-docker-image-configuration-try-runalikeSat, 17 Apr 2021 19:34:54 GMT<p>There is a chance that after entering the server, we do not know with what command docker image was launched. The <code class="language-text">docker inspect</code> will certainly come in handy, but the same one can little be unreadable - or it just requires the unreasonable rewriting of all values of the indicated result. Therefore, a simple image of assaflava / runlike was created which makes the work easier. The approach to the painting is simple. It is required to connect the socket docker and indicate the image. The output will be a printed result presented as a complete docker run command including all switches</p> <div class="gatsby-highlight" data-language="shell"><pre class="language-shell"><code class="language-shell">docker run --rm -v /var/run/docker.sock:/var/run/docker.sock <span class="token punctuation">\</span> assaflavie/runlike YOUR-CONTAINER</code></pre></div> <p>If we use a given command often, we can additionally add it as an alias</p> <div class="gatsby-highlight" data-language="shell"><pre class="language-shell"><code class="language-shell"><span class="token builtin class-name">alias</span> <span class="token assign-left variable">runlike</span><span class="token operator">=</span><span class="token string">"docker run --rm -v/var/run/docker.sock:/var/run/docker.sock assaflavie/runlike"</span> runlike YOUR-CONTAINER</code></pre></div> <p>A simple option, and effective. It allowed me to save time and find a solution to overwrite the image environment variable when on the server I couldn’t find the file that launched the original image.</p><![CDATA[Want privacy? Use multiple nicknames]]>https://sawczuk.dev/posts/want-privacy-use-multiple-nicknameshttps://sawczuk.dev/posts/want-privacy-use-multiple-nicknamesSat, 20 Mar 2021 02:31:23 GMT<p>At one point in being on the Internet and creating dozens of accounts, we are faced with one disturbing fact. Our internet presence is not anonymous. What to do with this fact, how to deal with it, and plan our anonymity on the Internet?</p> <p>As ordinary internet users, we are used to our nickname that we use every day. The problem begins if our nickname used in games is not separated from our so-called “private” accounts. Why? Let’s imagine that our in-game statement is quite controversial for one person we meet. Entering our nickname in the Google search engine itself may cause that the person who displayed results will indicate our profile on Facebook or LinkedIn. The same goes for forums like Reddit, the 9GAG community, or commenting on YouTube videos. The standard rule - “Nothing is lost on the Internet” and “If it’s on the internet, it’s no longer (only) yours” - is often overlooked in this case. However, it is worth remembering about additional security, such as having multiple nicknames, because thanks to this, we can feel comfortable on the Internet itself.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/36dd03a965c6b811e62deeb3963dd39e/47311/notes-2.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAQGAwX/xAAWAQEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAduhO0TS4wR//8QAGxAAAQQDAAAAAAAAAAAAAAAAAgEDBDEQETL/2gAIAQEAAQUCjO4VrajZclf/xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAwEBPwFn/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGhAAAgIDAAAAAAAAAAAAAAAAAAEQIRESkf/aAAgBAQAGPwLVllY4Kf/EABwQAQABBAMAAAAAAAAAAAAAAAEAECExQRFRof/aAAgBAQABPyEMAc4SOHXculp7j0FS/9oADAMBAAIAAwAAABDgH//EABcRAQEBAQAAAAAAAAAAAAAAAAEAEUH/2gAIAQMBAT8QHYXL/8QAFREBAQAAAAAAAAAAAAAAAAAAAQD/2gAIAQIBAT8QJC//xAAdEAACAgMAAwAAAAAAAAAAAAABIQARMVGxccHw/9oACAEBAAE/EAlslKejKuYoaqXlVwDxPlbmLwOicfc//9k='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/36dd03a965c6b811e62deeb3963dd39e/8ac56/notes-2.webp 240w, /static/36dd03a965c6b811e62deeb3963dd39e/d3be9/notes-2.webp 480w, /static/36dd03a965c6b811e62deeb3963dd39e/e46b2/notes-2.webp 960w, /static/36dd03a965c6b811e62deeb3963dd39e/260c2/notes-2.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/36dd03a965c6b811e62deeb3963dd39e/09b79/notes-2.jpg 240w, /static/36dd03a965c6b811e62deeb3963dd39e/7cc5e/notes-2.jpg 480w, /static/36dd03a965c6b811e62deeb3963dd39e/6a068/notes-2.jpg 960w, /static/36dd03a965c6b811e62deeb3963dd39e/47311/notes-2.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/36dd03a965c6b811e62deeb3963dd39e/6a068/notes-2.jpg" alt="Want privacy? Use multiple nicknames" title="Want privacy? Use multiple nicknames" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>It’s good to remember, that additionally, we have to watch out for leaks. If an application for food delivery leaks address data, and our login is quite widespread on the Internet, we can expect that after one of the worse matches in the MMO game, we can expect a letter from your teammate with a “thank you” in the mailbox. Alternatively, using a similar method, we can check whether the seller of the item at the auction is not trying to cheat us (Like when I wanted to buy an item for $ 300). By checking his nickname in Google, I found his Facebook and LinkedIn public profiles - and thanks to such targeting, I knew that the purchased item will be in good and tidy condition (apart from the fact that it was clear where he often dined, what cuisine he likes and many other pieces of information… ).</p> <p>An additional problem that can happen to us in the life of an online person is having all accounts attached to one email. As far as it is, it’s not about the risk of hacking our account and extracting data by using the password reminder method. We must remember that we are also dependent on one mail provider. Quite recently, at the beginning of 2021, it happened that Google services stopped working for a while. What if access to our data suddenly disappears? The second bad news is a shared account in Google services. What if we get banned from YouTube? Our account with videos will be lost - well, it happens if we accidentally break some rules or get a Copywrite strike 3 times. However, it turns out that the ban on the account does not stop only on the YouTube service - it affects our full private Google account - where we keep our photos, contacts, e-mail, integration with phones, browsing history, and have payment cards attached. All this can be lost like <a href="https://support.google.com/accounts/thread/41351460?hl=en" target="_blank" rel="nofollow noopener noreferrer">for a given user of the portal</a>.</p> <p>Recently, I started to use the principle of separation of accounts and try to migrate data. Some tips to start:</p> <ul> <li> <p>Separate nickname and email</p> <ul> <li>private accounts which I intentionally sign with my names, such as photography or blog</li> <li>discussion forums (Reddit)</li> <li>shopping accounts (Amazon / AliExpress)</li> <li>games and libraries</li> <li>video content created (Twitch / Youtube)</li> </ul> </li> <li>Just in case, I recommend sharing mailboxes between different providers, such as the main Google for our personal contacts, but also e.g. proton mail for everyone else</li> <li>We may have our password bank in the cloud, but I recommend that you synchronize passwords with the KeePass file from time to time</li> </ul> <p>Maybe the method works and I will not be afraid of unambiguously connecting my nickname in the mobile game with my private person in the first 3 minutes of any match. Time will tell, for now, I am adjusting the results and checking to what extent the given pool of accounts and addresses is sufficient and not burdensome for me.</p><![CDATA[Queuing jobs in apps is easier than you think]]>https://sawczuk.dev/posts/queuing-jobs-in-apps-is-easier-than-you-thinkhttps://sawczuk.dev/posts/queuing-jobs-in-apps-is-easier-than-you-thinkThu, 10 Dec 2020 00:47:47 GMT<p>When trying to communicate with our application, we always expect that it is available and can handle our request at a given moment. However, if these things are not certain, or we need to operate in a distributed structure with no known source where the request is to go, we can use task queuing.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/a195be209e8166f7d324a006a81f5352/47311/server-8.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAUBAwT/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAABWWmmFRJX/8QAGxAAAQQDAAAAAAAAAAAAAAAAAAECAyEEMjP/2gAIAQEAAQUCZayUKpD0yNXH/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFRABAQAAAAAAAAAAAAAAAAAAESD/2gAIAQEABj8CK//EABgQAQEBAQEAAAAAAAAAAAAAAAEAMRFx/9oACAEBAAE/IUhOgKjN2wsfbV//2gAMAwEAAgADAAAAEMsP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHhABAAICAQUAAAAAAAAAAAAAAQAhETFRYXGhwfD/2gAIAQEAAT8QQyZ0PEDkGitYJeq5b3PCZ8XSae3uf//Z'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/a195be209e8166f7d324a006a81f5352/8ac56/server-8.webp 240w, /static/a195be209e8166f7d324a006a81f5352/d3be9/server-8.webp 480w, /static/a195be209e8166f7d324a006a81f5352/e46b2/server-8.webp 960w, /static/a195be209e8166f7d324a006a81f5352/260c2/server-8.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/a195be209e8166f7d324a006a81f5352/09b79/server-8.jpg 240w, /static/a195be209e8166f7d324a006a81f5352/7cc5e/server-8.jpg 480w, /static/a195be209e8166f7d324a006a81f5352/6a068/server-8.jpg 960w, /static/a195be209e8166f7d324a006a81f5352/47311/server-8.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/a195be209e8166f7d324a006a81f5352/6a068/server-8.jpg" alt="Knowledge books: Python" title="Knowledge books: Python" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>Queuing is most often used when starting actions that require more processing time and are not required to get immediate results. Therefore, when writing various types of websites, the handling of these orders is triggered in separate processes. The very implementation of the queuing system into your project is also not difficult.</p> <h2 id="producers--consumers" style="position:relative;"><a href="#producers--consumers" aria-label="producers consumers permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Producers &#x26; Consumers</h2> <p>Queuing uses two modes. In the standard understanding of the process, it is sending a message to a queue created and selected by the user, and the other is reading the stored messages. Messages can be additionally tagged and sorted into appropriate collections working on one queue. Depending on the need to use the queuing server itself, you may want to consider a couple of data sending and reading patterns. This information can be found both in the updates of the tools themselves and in popular forums for developers.</p> <p>One of the most popular queuing apps &#x26; services that are simple to set up and easy to use:</p> <ul> <li><a href="https://www.rabbitmq.com/" target="_blank" rel="nofollow noopener noreferrer">RabbitMQ</a></li> <li><a href="https://redis.io/topics/pubsub" target="_blank" rel="nofollow noopener noreferrer">Redis</a></li> <li><a href="https://kafka.apache.org/" target="_blank" rel="nofollow noopener noreferrer">Kafka</a></li> <li><a href="http://activemq.apache.org/" target="_blank" rel="nofollow noopener noreferrer">ActiveMQ</a></li> <li><a href="https://zeromq.org/" target="_blank" rel="nofollow noopener noreferrer">ZeroMQ</a></li> <li><a href="https://cloud.google.com/pubsub/docs" target="_blank" rel="nofollow noopener noreferrer">Google Cloud Pub / Sub</a></li> <li><a href="https://aws.amazon.com/sqs/" target="_blank" rel="nofollow noopener noreferrer">Amazon Simple Queue Service</a></li> </ul><![CDATA[Knowledge books: Python]]>https://sawczuk.dev/posts/knowledge-books-pythonhttps://sawczuk.dev/posts/knowledge-books-pythonTue, 01 Dec 2020 00:39:21 GMT<p><em><strong>Knowledge books</strong> is a mini series of constantly updated articles in which I share my more interesting discoveries</em></p> <p>Each language has its own nice extras. The same applies to the functions and usability of the basic functions of the programs. Of course, the important thing is that if some parts are not used on a daily basis, they may be forgotten. Therefore, here are some brief notes on this language functions and methods of implantation. Written knowledge cannot be forgotten.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/edc34f018a6b7e4cb62fd0ce2dac7bec/47311/server-7.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMFBP/EABUBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAGRpnNKSAz/AP/EABkQAQEAAwEAAAAAAAAAAAAAAAEAAgMTQf/aAAgBAQABBQLU4i80i8v/xAAWEQADAAAAAAAAAAAAAAAAAAABAhD/2gAIAQMBAT8BUz//xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAgEBPwFn/8QAGhAAAQUBAAAAAAAAAAAAAAAAAQACECGBEf/aAAgBAQAGPwK+4jeFsmP/xAAcEAACAQUBAAAAAAAAAAAAAAAAATEQEVGRsfH/2gAIAQEAAT8hc3vWGYY+kckjlT//2gAMAwEAAgADAAAAEDcP/8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQMBAT8QUEn/xAAWEQEBAQAAAAAAAAAAAAAAAAABERD/2gAIAQIBAT8QFjn/xAAbEAEAAQUBAAAAAAAAAAAAAAABEQAQITFxQf/aAAgBAQABPxAwJxhOHRM0r2vhq8GuzUJRrywWn//Z'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/edc34f018a6b7e4cb62fd0ce2dac7bec/8ac56/server-7.webp 240w, /static/edc34f018a6b7e4cb62fd0ce2dac7bec/d3be9/server-7.webp 480w, /static/edc34f018a6b7e4cb62fd0ce2dac7bec/e46b2/server-7.webp 960w, /static/edc34f018a6b7e4cb62fd0ce2dac7bec/260c2/server-7.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/edc34f018a6b7e4cb62fd0ce2dac7bec/09b79/server-7.jpg 240w, /static/edc34f018a6b7e4cb62fd0ce2dac7bec/7cc5e/server-7.jpg 480w, /static/edc34f018a6b7e4cb62fd0ce2dac7bec/6a068/server-7.jpg 960w, /static/edc34f018a6b7e4cb62fd0ce2dac7bec/47311/server-7.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/edc34f018a6b7e4cb62fd0ce2dac7bec/6a068/server-7.jpg" alt="Knowledge books: Python" title="Knowledge books: Python" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>Table of Contents:</p> <ul> <li><a href="#array-operations">Array operations</a></li> <li><a href="#code-improvements">Code improvements</a></li> <li><a href="#command-line-arguments">Command line arguments</a></li> <li><a href="#decorators">Decorators</a></li> <li><a href="#docstring">Docstring</a></li> <li> <p><a href="#functions">Functions</a></p> <ul> <li><a href="#mutable-default-parameter-values">Mutable Default Parameter Values</a></li> <li><a href="#mutable-objects">Mutable Objects</a></li> <li><a href="#args">Args</a></li> <li><a href="#argument-tuple-unpacking">Argument Tuple Unpacking</a></li> <li><a href="#argument-dictionary-packing">Argument Dictionary Packing</a></li> <li><a href="#keyword-only-any-arguments">Keyword-Only Any Arguments</a></li> <li><a href="#keyword-only-no-additional-arguments">Keyword-Only No Additional Arguments</a></li> <li><a href="#python-function-annotations">Python Function Annotations</a></li> </ul> </li> <li><a href="#magic-attributes">Magic attributes</a></li> <li> <p><a href="#loops--enumerate">Loops &#x26; Enumerate</a></p> <ul> <li><a href="#content-loop">Content loop</a></li> <li><a href="#content-loop-with-manual-index">Content loop with manual index</a></li> <li><a href="#range-loop">Range loop</a></li> <li><a href="#enumerate-loop">Enumerate loop</a></li> <li><a href="#enumerate-example-usage">Enumerate example usage</a></li> <li><a href="#array-strip-examples">Array strip examples</a></li> <li><a href="#using-iterator-in-enumerate">Using iterator in enumerate</a></li> </ul> </li> <li><a href="#object-oriented-programming-oop">Object-oriented programming</a></li> <li><a href="#other-topics">Other topics</a></li> <li><a href="#opening-file-streams">Opening file streams</a></li> <li><a href="#practice-materials">Practice materials</a></li> <li><a href="#references">References</a></li> <li><a href="#return-statements--generators">Return statements &#x26; generators</a></li> <li> <p><a href="#string-formatting">String formatting</a></p> <ul> <li><a href="#old-style-string-formatting--operator">Old Style” String Formatting (% Operator)</a></li> <li><a href="#new-style-string-formatting-strformat">“New Style” String Formatting (str.format)</a></li> <li><a href="#string-interpolation--f-strings-python-36">String Interpolation / f-Strings (Python 3.6+)</a></li> <li><a href="#template-strings-standard-library">Template Strings (Standard Library)</a></li> </ul> </li> <li><a href="#testing">Testing</a></li> <li><a href="#threading">Threading</a></li> </ul> <h1 id="array-operations" style="position:relative;"><a href="#array-operations" aria-label="array operations permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Array operations</h1> <ul> <li><a href="https://realpython.com/python-map-function/" target="_blank" rel="nofollow noopener noreferrer">Python’s map(): Processing Iterables Without a Loop</a></li> <li><a href="https://realpython.com/python-reduce-function/" target="_blank" rel="nofollow noopener noreferrer">Python’s reduce(): From Functional to Pythonic Style</a></li> </ul> <h1 id="code-improvements" style="position:relative;"><a href="#code-improvements" aria-label="code improvements permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Code improvements</h1> <ul> <li><a href="https://realpython.com/python-code-quality/" target="_blank" rel="nofollow noopener noreferrer">Python Code Quality: Tools &#x26; Best Practices</a></li> <li><a href="https://realpython.com/documenting-python-code/" target="_blank" rel="nofollow noopener noreferrer">Documenting Python Code: A Complete Guide</a></li> <li><a href="https://realpython.com/python-application-layouts/" target="_blank" rel="nofollow noopener noreferrer">Python Application Layouts: A Reference</a></li> <li><a href="https://realpython.com/python-logging-source-code/" target="_blank" rel="nofollow noopener noreferrer">Python Logging: A Stroll Through the Source Code</a></li> <li><a href="https://realpython.com/python-refactoring/" target="_blank" rel="nofollow noopener noreferrer">Refactoring Python Applications for Simplicity</a></li> <li><a href="https://realpython.com/python-pep8/" target="_blank" rel="nofollow noopener noreferrer">How to Write Beautiful Python Code With PEP 8</a></li> <li><a href="https://realpython.com/the-most-diabolical-python-antipattern/" target="_blank" rel="nofollow noopener noreferrer">The Most Diabolical Python Antipattern</a></li> </ul> <h1 id="command-line-arguments" style="position:relative;"><a href="#command-line-arguments" aria-label="command line arguments permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Command line arguments</h1> <ul> <li><a href="https://realpython.com/python-command-line-arguments/" target="_blank" rel="nofollow noopener noreferrer">Python Command Line Arguments</a></li> </ul> <h1 id="decorators" style="position:relative;"><a href="#decorators" aria-label="decorators permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Decorators</h1> <ul> <li><a href="https://realpython.com/primer-on-python-decorators/" target="_blank" rel="nofollow noopener noreferrer">Primer on Python Decorators</a></li> </ul> <h1 id="docstring" style="position:relative;"><a href="#docstring" aria-label="docstring permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Docstring</h1> <ul> <li><a href="https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_numpy.html#example-numpy" target="_blank" rel="nofollow noopener noreferrer">Example NumPy Style Python Docstrings</a></li> </ul> <h1 id="functions" style="position:relative;"><a href="#functions" aria-label="functions permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Functions</h1> <ul> <li><a href="https://realpython.com/defining-your-own-python-function/" target="_blank" rel="nofollow noopener noreferrer">Defining Your Own Python Function</a></li> <li><a href="https://realpython.com/python-lambda/" target="_blank" rel="nofollow noopener noreferrer">How to Use Python Lambda Functions</a></li> </ul> <h4 id="mutable-default-parameter-values" style="position:relative;"><a href="#mutable-default-parameter-values" aria-label="mutable default parameter values permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Mutable Default Parameter Values</h4> <p>In Python, default parameter values are defined only once when the function is defined (that is, when the def statement is executed). The default value isn’t re-defined each time the function is called. Thus, each time you call f() without a parameter, you’re performing .append() on the same list.</p> <div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">f</span><span class="token punctuation">(</span>my_list <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">:</span> my_list<span class="token punctuation">.</span>append<span class="token punctuation">(</span><span class="token string">'###'</span><span class="token punctuation">)</span> <span class="token keyword">return</span> my_list f<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment"># ['###']</span> f<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment"># ['###', '###']</span></code></pre></div> <p>As a workaround, consider using a default argument value that signals no argument has been specified. Most any value would work, but None is a common choice.</p> <div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">f</span><span class="token punctuation">(</span>my_list<span class="token operator">=</span><span class="token boolean">None</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">if</span> my_list <span class="token keyword">is</span> <span class="token boolean">None</span><span class="token punctuation">:</span> my_list <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> my_list<span class="token punctuation">.</span>append<span class="token punctuation">(</span><span class="token string">'###'</span><span class="token punctuation">)</span> <span class="token keyword">return</span> my_list</code></pre></div> <h4 id="mutable-objects" style="position:relative;"><a href="#mutable-objects" aria-label="mutable objects permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Mutable Objects</h4> <p>Argument passing in Python can be summarized as follows. <strong>Passing an immutable object</strong>, like an int, str, tuple, or frozenset, to a Python function acts like pass-by-value. The function can’t modify the object in the calling environment.</p> <p><strong>Passing a mutable object</strong> such as a list, dict, or set acts somewhat—but not exactly—like pass-by-reference. The function can’t reassign the object wholesale, but it can change items in place within the object, and these changes will be reflected in the calling environment.</p> <h4 id="args" style="position:relative;"><a href="#args" aria-label="args permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Args</h4> <div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token operator">*</span>args<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">print</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token builtin">type</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token builtin">len</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">for</span> x <span class="token keyword">in</span> args<span class="token punctuation">:</span> <span class="token keyword">print</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span> f<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">)</span> <span class="token comment"># (1, 2, 3) </span> <span class="token comment"># &lt;class 'tuple'> 3</span> <span class="token comment"># 1</span> <span class="token comment"># 2</span> <span class="token comment"># 3</span></code></pre></div> <h4 id="argument-tuple-unpacking" style="position:relative;"><a href="#argument-tuple-unpacking" aria-label="argument tuple unpacking permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Argument Tuple Unpacking</h4> <p>Although this type of unpacking is called tuple unpacking, it doesn’t only work with tuples. The asterisk (*) operator can be applied to any iterable in a Python function call. For example, a list or set can be unpacked as well</p> <div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">f</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">,</span> z<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f'x = </span><span class="token interpolation"><span class="token punctuation">{</span>x<span class="token punctuation">}</span></span><span class="token string">'</span></span><span class="token punctuation">)</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f'y = </span><span class="token interpolation"><span class="token punctuation">{</span>y<span class="token punctuation">}</span></span><span class="token string">'</span></span><span class="token punctuation">)</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f'z = </span><span class="token interpolation"><span class="token punctuation">{</span>z<span class="token punctuation">}</span></span><span class="token string">'</span></span><span class="token punctuation">)</span> f<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">)</span> <span class="token comment"># x = 1</span> <span class="token comment"># y = 2</span> <span class="token comment"># z = 3</span> t <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token string">'foo'</span><span class="token punctuation">,</span> <span class="token string">'bar'</span><span class="token punctuation">,</span> <span class="token string">'baz'</span><span class="token punctuation">)</span> f<span class="token punctuation">(</span><span class="token operator">*</span>t<span class="token punctuation">)</span> <span class="token comment"># x = foo</span> <span class="token comment"># y = bar</span> <span class="token comment"># z = baz</span></code></pre></div> <h4 id="argument-dictionary-packing" style="position:relative;"><a href="#argument-dictionary-packing" aria-label="argument dictionary packing permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Argument Dictionary Packing</h4> <div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token operator">**</span>kwargs<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">print</span><span class="token punctuation">(</span>kwargs<span class="token punctuation">)</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token builtin">type</span><span class="token punctuation">(</span>kwargs<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">for</span> key<span class="token punctuation">,</span> val <span class="token keyword">in</span> kwargs<span class="token punctuation">.</span>items<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">print</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> <span class="token string">'->'</span><span class="token punctuation">,</span> val<span class="token punctuation">)</span> f<span class="token punctuation">(</span>foo<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">,</span> bar<span class="token operator">=</span><span class="token number">2</span><span class="token punctuation">,</span> baz<span class="token operator">=</span><span class="token number">3</span><span class="token punctuation">)</span> <span class="token comment"># {'foo': 1, 'bar': 2, 'baz': 3}</span> <span class="token comment"># &lt;class 'dict'></span> <span class="token comment"># foo -> 1</span> <span class="token comment"># bar -> 2</span> <span class="token comment"># baz -> 3</span></code></pre></div> <h4 id="keyword-only-any-arguments" style="position:relative;"><a href="#keyword-only-any-arguments" aria-label="keyword only any arguments permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Keyword-Only Any Arguments</h4> <div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">oper</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">,</span> <span class="token operator">*</span>ignore<span class="token punctuation">,</span> op<span class="token operator">=</span><span class="token string">'+'</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">if</span> op <span class="token operator">==</span> <span class="token string">'+'</span><span class="token punctuation">:</span> <span class="token keyword">return</span> x <span class="token operator">+</span> y <span class="token keyword">elif</span> op <span class="token operator">==</span> <span class="token string">'-'</span><span class="token punctuation">:</span> <span class="token keyword">return</span> x <span class="token operator">-</span> y <span class="token keyword">elif</span> op <span class="token operator">==</span> <span class="token string">'/'</span><span class="token punctuation">:</span> <span class="token keyword">return</span> x <span class="token operator">/</span> y <span class="token keyword">else</span><span class="token punctuation">:</span> <span class="token keyword">return</span> <span class="token boolean">None</span> oper<span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">,</span> op<span class="token operator">=</span><span class="token string">'+'</span><span class="token punctuation">)</span> <span class="token comment"># 7</span> oper<span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">,</span> op<span class="token operator">=</span><span class="token string">'/'</span><span class="token punctuation">)</span> <span class="token comment"># 0.75</span> oper<span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">,</span> <span class="token string">"I don't belong here"</span><span class="token punctuation">)</span> <span class="token comment"># 7</span> oper<span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">,</span> <span class="token string">"I don't belong here"</span><span class="token punctuation">,</span> op<span class="token operator">=</span><span class="token string">'/'</span><span class="token punctuation">)</span> <span class="token comment"># 0.75</span></code></pre></div> <h4 id="keyword-only-no-additional-arguments" style="position:relative;"><a href="#keyword-only-no-additional-arguments" aria-label="keyword only no additional arguments permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Keyword-Only No Additional Arguments</h4> <div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">oper</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">,</span> <span class="token operator">*</span><span class="token punctuation">,</span> op<span class="token operator">=</span><span class="token string">'+'</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">if</span> op <span class="token operator">==</span> <span class="token string">'+'</span><span class="token punctuation">:</span> <span class="token keyword">return</span> x <span class="token operator">+</span> y <span class="token keyword">elif</span> op <span class="token operator">==</span> <span class="token string">'-'</span><span class="token punctuation">:</span> <span class="token keyword">return</span> x <span class="token operator">-</span> y <span class="token keyword">elif</span> op <span class="token operator">==</span> <span class="token string">'/'</span><span class="token punctuation">:</span> <span class="token keyword">return</span> x <span class="token operator">/</span> y <span class="token keyword">else</span><span class="token punctuation">:</span> <span class="token keyword">return</span> <span class="token boolean">None</span> oper<span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">,</span> op<span class="token operator">=</span><span class="token string">'+'</span><span class="token punctuation">)</span> <span class="token comment"># 7</span> oper<span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">,</span> op<span class="token operator">=</span><span class="token string">'/'</span><span class="token punctuation">)</span> <span class="token comment"># 0.75</span> oper<span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">,</span> <span class="token string">"I don't belong here"</span><span class="token punctuation">)</span> <span class="token comment"># Traceback (most recent call last):</span> <span class="token comment"># File "&lt;stdin>", line 1, in &lt;module></span> <span class="token comment"># TypeError: oper() takes 2 positional arguments but 3 were given</span> oper<span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">,</span> <span class="token string">'+'</span><span class="token punctuation">)</span> <span class="token comment"># Traceback (most recent call last):</span> <span class="token comment"># File "&lt;stdin>", line 1, in &lt;module></span> <span class="token comment"># TypeError: oper() takes 2 positional arguments but 3 were given</span></code></pre></div> <h4 id="python-function-annotations" style="position:relative;"><a href="#python-function-annotations" aria-label="python function annotations permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Python Function Annotations</h4> <p>Annotations don’t impose any semantic restrictions on the code whatsoever. They’re simply bits of metadata attached to the Python function parameters and return value. Python dutifully stashes them in a dictionary, assigns the dictionary to the function’s <code class="language-text">__annotations__</code> dunder attribute, and that’s it. Annotations are completely optional and don’t have any impact on Python function execution at all.</p> <p>To quote Amahl in Amahl and the Night Visitors, “What’s the use of having it then?”</p> <p>For starters, annotations make good documentation. You can specify the same information in the docstring, of course, but placing it directly in the function definition adds clarity. </p> <div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">f</span><span class="token punctuation">(</span>a<span class="token punctuation">:</span> <span class="token builtin">int</span> <span class="token operator">=</span> <span class="token number">12</span><span class="token punctuation">,</span> b<span class="token punctuation">:</span> <span class="token builtin">str</span> <span class="token operator">=</span> <span class="token string">'baz'</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">float</span><span class="token punctuation">:</span> <span class="token keyword">print</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> b<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token number">3.5</span> f<span class="token punctuation">.</span>__annotations__ <span class="token comment"># {'a': &lt;class 'int'>, 'b': &lt;class 'str'>, 'return': &lt;class 'float'>}</span></code></pre></div> <h1 id="magic-attributes" style="position:relative;"><a href="#magic-attributes" aria-label="magic attributes permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Magic attributes</h1> <ul> <li><a href="https://rszalski.github.io/magicmethods/" target="_blank" rel="nofollow noopener noreferrer">A Guide to Python’s Magic Methods</a></li> <li><a href="https://rszalski.github.io/magicmethods/#appendix1" target="_blank" rel="nofollow noopener noreferrer">How to Call Magic Methods</a></li> </ul> <p>Magic Methods:</p> <ul> <li> <p>Construction and Initialization</p> <ul> <li><code class="language-text">__init__(self, [...)</code> The initializer for the class. It gets passed whatever the primary constructor was called with</li> <li><code class="language-text">__new__(cls, [...)</code> Is the first method to get called in an object’s instantiation. It takes the class, then any other arguments that it will pass along to <code class="language-text">__init__</code>. <code class="language-text">__new__</code> is used fairly rarely, but it does have its purposes, particularly when subclassing an immutable type like a tuple or a string.</li> <li><code class="language-text">__del__(self)</code> It defines behavior for when an object is garbage collected. It can be quite useful for objects that might require extra cleanup upon deletion, like sockets or file objects.</li> </ul> </li> <li> <p>Comparison magic methods</p> <ul> <li><code class="language-text">__cmp__(self, other)</code> Is the most basic of the comparison magic methods. It actually implements behavior for all of the comparison operators (&#x3C;, ==, !=, etc.). Should return a negative integer if self &#x3C; other, zero if self == other, and positive if self > other</li> <li><code class="language-text">__eq__(self, other)</code> Behavior: equality operator, <code class="language-text">==</code></li> <li><code class="language-text">__nq__(self, other)</code> Behavior: inequality operator, <code class="language-text">!=</code></li> <li><code class="language-text">__lt__(self, other)</code> Behavior: less-than operator, <code class="language-text">&lt;</code></li> <li><code class="language-text">__gt__(self, other)</code> Behavior: greater-than operator, <code class="language-text">&gt;</code></li> <li><code class="language-text">__le__(self, other)</code> Behavior: less-than-or-equal-to operator, <code class="language-text">&lt;=</code></li> <li><code class="language-text">__ge__(self, other)</code> Behavior: greater-than-or-equal-to operator, <code class="language-text">&gt;=</code></li> </ul> </li> <li> <p>Numeric magic methods</p> <ul> <li>Unary operators and functions</li> <li>Normal arithmetic operators</li> <li>Reflected arithmetic operators <code class="language-text">some_object + other</code> That was “normal” addition. The reflected equivalent is the same thing, except with the operands switched around: <code class="language-text">other + some_object</code></li> <li>Augmented assignment</li> <li>Type conversion magic methods</li> </ul> </li> <li> <p>Representing your Classes</p> <ul> <li><code class="language-text">__str__(self)</code> Defines behavior for when str() is called on an instance of your class.</li> <li><code class="language-text">__repr__(self)</code> The major difference between str() and repr() is intended audience. repr() is intended to produce output that is mostly machine-readable (in many cases, it could be valid Python code even), whereas str() is intended to be human-readable.</li> <li><code class="language-text">__hash__(self)</code> </li> </ul> </li> <li> <p>Controlling Attribute Access</p> <ul> <li><code class="language-text">__getattr__(self, name)</code></li> <li><code class="language-text">__setattr__(self, name, value)</code></li> <li><code class="language-text">__delattr__(self, name)</code></li> </ul> </li> <li> <p>Making Custom Sequences</p> <ul> <li>The magic behind containers</li> </ul> </li> <li>Reflection</li> <li> <p>Callable Objects</p> <ul> <li><code class="language-text">__call__(self, [args...])</code> Allows an instance of a class to be called as a function. Essentially, this means that x() is the same as <code class="language-text">x.__call__()</code></li> </ul> </li> <li> <p>Context Managers</p> <ul> <li><code class="language-text">__enter__(self)</code> Defines what the context manager should do at the beginning of the block created by the with statement. Note that the return value of <strong>enter</strong> is bound to the target of the with statement, or the name after the as.</li> <li><code class="language-text">__exit__(self, exception_type, exception_value, traceback)</code></li> </ul> </li> <li> <p>Building Descriptor Objects</p> <ul> <li><code class="language-text">__get__(self, instance, owner)</code> Define behavior for when the descriptor’s value is retrieved. instance is the instance of the owner object. owner is the owner class itself.</li> <li><code class="language-text">__set__(self, instance, value)</code></li> <li><code class="language-text">__delete__(self, instance)</code></li> </ul> </li> <li> <p>Copying</p> <ul> <li><code class="language-text">__copy__(self)</code></li> <li><code class="language-text">__deepcopy__(self, memodict={})</code></li> </ul> </li> <li> <p>Other</p> <ul> <li><code class="language-text">__annotations__(self)</code></li> <li><code class="language-text">__doc__(self)</code></li> </ul> </li> </ul> <h1 id="loops--enumerate" style="position:relative;"><a href="#loops--enumerate" aria-label="loops enumerate permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Loops &#x26; Enumerate</h1> <ul> <li><a href="https://realpython.com/python-enumerate/" target="_blank" rel="nofollow noopener noreferrer">Python enumerate(): Simplify Looping With Counters</a></li> </ul> <p>✓ Use Python’s enumerate() in your for loops<br> ✓ Get values from enumerate() using argument unpacking<br> ✓ Implement your own equivalent function to enumerate()\</p> <p><em>Technical Detail: According to the Python documentation, an iterable is any object that can return its members one at a time. By definition, iterables support the iterator protocol, which specifies how object members are returned when an object is used in an iterator. Python has two commonly used types of iterables:</em></p> <ul> <li><a href="https://docs.python.org/3/glossary.html#term-sequence" target="_blank" rel="nofollow noopener noreferrer"><em>Sequences</em></a></li> <li><a href="https://docs.python.org/3/glossary.html#term-generator" target="_blank" rel="nofollow noopener noreferrer"><em>Generators</em></a></li> </ul> <h4 id="content-loop" style="position:relative;"><a href="#content-loop" aria-label="content loop permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Content loop</h4> <div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python">values <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"a"</span><span class="token punctuation">,</span> <span class="token string">"b"</span><span class="token punctuation">,</span> <span class="token string">"c"</span><span class="token punctuation">]</span> <span class="token keyword">for</span> value <span class="token keyword">in</span> values<span class="token punctuation">:</span> <span class="token keyword">print</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span> <span class="token comment"># a</span> <span class="token comment"># b</span> <span class="token comment"># c</span></code></pre></div> <h4 id="content-loop-with-manual-index" style="position:relative;"><a href="#content-loop-with-manual-index" aria-label="content loop with manual index permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Content loop with manual index</h4> <div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python">index <span class="token operator">=</span> <span class="token number">0</span> values <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"a"</span><span class="token punctuation">,</span> <span class="token string">"b"</span><span class="token punctuation">,</span> <span class="token string">"c"</span><span class="token punctuation">]</span> <span class="token keyword">for</span> value <span class="token keyword">in</span> values<span class="token punctuation">:</span> <span class="token keyword">print</span><span class="token punctuation">(</span>index<span class="token punctuation">,</span> value<span class="token punctuation">)</span> index <span class="token operator">+=</span> <span class="token number">1</span> <span class="token comment"># 0 a</span> <span class="token comment"># 1 b</span> <span class="token comment"># 2 c</span></code></pre></div> <h4 id="range-loop" style="position:relative;"><a href="#range-loop" aria-label="range loop permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Range loop</h4> <div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python">values <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"a"</span><span class="token punctuation">,</span> <span class="token string">"b"</span><span class="token punctuation">,</span> <span class="token string">"c"</span><span class="token punctuation">]</span> <span class="token keyword">for</span> index <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token builtin">len</span><span class="token punctuation">(</span>values<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">:</span> value <span class="token operator">=</span> values<span class="token punctuation">[</span>index<span class="token punctuation">]</span> <span class="token keyword">print</span><span class="token punctuation">(</span>index<span class="token punctuation">,</span> value<span class="token punctuation">)</span></code></pre></div> <p>You should use enumerate() anytime you need to use the count and an item in a loop. Keep in mind that enumerate() increments the count by one on every iteration. </p> <h4 id="enumerate-loop" style="position:relative;"><a href="#enumerate-loop" aria-label="enumerate loop permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Enumerate loop</h4> <div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python">values <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"a"</span><span class="token punctuation">,</span> <span class="token string">"b"</span><span class="token punctuation">,</span> <span class="token string">"c"</span><span class="token punctuation">]</span> <span class="token keyword">for</span> count<span class="token punctuation">,</span> value <span class="token keyword">in</span> <span class="token builtin">enumerate</span><span class="token punctuation">(</span>values<span class="token punctuation">,</span> start<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">print</span><span class="token punctuation">(</span>count<span class="token punctuation">,</span> value<span class="token punctuation">)</span></code></pre></div> <h4 id="enumerate-example-usage" style="position:relative;"><a href="#enumerate-example-usage" aria-label="enumerate example usage permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Enumerate example usage</h4> <div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">even_items</span><span class="token punctuation">(</span>iterable<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token triple-quoted-string string">"""Return items from ``iterable`` when their index is even."""</span> values <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token keyword">for</span> index<span class="token punctuation">,</span> value <span class="token keyword">in</span> <span class="token builtin">enumerate</span><span class="token punctuation">(</span>iterable<span class="token punctuation">,</span> start<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">if</span> <span class="token keyword">not</span> index <span class="token operator">%</span> <span class="token number">2</span><span class="token punctuation">:</span> values<span class="token punctuation">.</span>append<span class="token punctuation">(</span>value<span class="token punctuation">)</span> <span class="token keyword">return</span> values</code></pre></div> <h4 id="array-strip-examples" style="position:relative;"><a href="#array-strip-examples" aria-label="array strip examples permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Array strip examples</h4> <div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">even_items</span><span class="token punctuation">(</span>iterable<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">return</span> <span class="token punctuation">[</span>v <span class="token keyword">for</span> i<span class="token punctuation">,</span> v <span class="token keyword">in</span> <span class="token builtin">enumerate</span><span class="token punctuation">(</span>iterable<span class="token punctuation">,</span> start<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token keyword">if</span> <span class="token keyword">not</span> i <span class="token operator">%</span> <span class="token number">2</span><span class="token punctuation">]</span> alphabet <span class="token operator">=</span> <span class="token string">"abcdefghijklmnopqrstuvwxyz"</span> even_items<span class="token punctuation">(</span>alphabet<span class="token punctuation">)</span> <span class="token comment"># ['b', 'd', 'f', 'h', 'j', 'l', 'n', 'p', 'r', 't', 'v', 'x', 'z']</span> <span class="token builtin">list</span><span class="token punctuation">(</span>alphabet<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token comment"># ['b', 'd', 'f', 'h', 'j', 'l', 'n', 'p', 'r', 't', 'v', 'x', 'z']</span></code></pre></div> <h4 id="using-iterator-in-enumerate" style="position:relative;"><a href="#using-iterator-in-enumerate" aria-label="using iterator in enumerate permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Using iterator in enumerate</h4> <div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">alphabet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> alpha <span class="token operator">=</span> <span class="token string">"abcdefghijklmnopqrstuvwxyz"</span> <span class="token keyword">for</span> a <span class="token keyword">in</span> alpha<span class="token punctuation">:</span> <span class="token keyword">yield</span> a alphabet<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token number">2</span><span class="token punctuation">]</span> <span class="token comment"># Traceback (most recent call last):</span> <span class="token comment"># File "&lt;stdin>", line 1, in &lt;module></span> <span class="token comment"># TypeError: 'function' object is not subscriptable</span> even_items<span class="token punctuation">(</span>alphabet<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment"># ['b', 'd', 'f', 'h', 'j', 'l', 'n', 'p', 'r', 't', 'v', 'x', 'z']</span></code></pre></div> <p>When you call enumerate() and pass a sequence of values, Python returns an iterator. When you ask the iterator for its next value, it yields a tuple with two elements. The first element of the tuple is the count, and the second element is the value from the sequence that you passed:</p> <div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python">values <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"a"</span><span class="token punctuation">,</span> <span class="token string">"b"</span><span class="token punctuation">]</span> enum_instance <span class="token operator">=</span> <span class="token builtin">enumerate</span><span class="token punctuation">(</span>values<span class="token punctuation">)</span> enum_instance <span class="token comment"># &lt;enumerate at 0x7fe75d728180></span> <span class="token builtin">next</span><span class="token punctuation">(</span>enum_instance<span class="token punctuation">)</span> <span class="token comment"># (0, 'a')</span> <span class="token builtin">next</span><span class="token punctuation">(</span>enum_instance<span class="token punctuation">)</span> <span class="token comment"># (1, 'b')</span> <span class="token builtin">next</span><span class="token punctuation">(</span>enum_instance<span class="token punctuation">)</span> <span class="token comment"># Traceback (most recent call last):</span> <span class="token comment"># File "&lt;stdin>", line 1, in &lt;module></span> <span class="token comment"># StopIteration</span></code></pre></div> <p>In the for loop in this example, you nest zip() inside enumerate(). This means that each time the for loop iterates, enumerate() yields a tuple with the first value as the count and the second value as another tuple containing the elements from the arguments to zip(). To unpack the nested structure, you need to add parentheses to capture the elements from the nested tuple of elements from zip().</p> <div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python">first <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"a"</span><span class="token punctuation">,</span> <span class="token string">"b"</span><span class="token punctuation">,</span> <span class="token string">"c"</span><span class="token punctuation">]</span> second <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"d"</span><span class="token punctuation">,</span> <span class="token string">"e"</span><span class="token punctuation">,</span> <span class="token string">"f"</span><span class="token punctuation">]</span> third <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"g"</span><span class="token punctuation">,</span> <span class="token string">"h"</span><span class="token punctuation">,</span> <span class="token string">"i"</span><span class="token punctuation">]</span> <span class="token keyword">for</span> one<span class="token punctuation">,</span> two<span class="token punctuation">,</span> three <span class="token keyword">in</span> <span class="token builtin">zip</span><span class="token punctuation">(</span>first<span class="token punctuation">,</span> second<span class="token punctuation">,</span> third<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">print</span><span class="token punctuation">(</span>one<span class="token punctuation">,</span> two<span class="token punctuation">,</span> three<span class="token punctuation">)</span> <span class="token comment"># a d g</span> <span class="token comment"># b e h</span> <span class="token comment"># c f i</span> <span class="token keyword">for</span> count<span class="token punctuation">,</span> <span class="token punctuation">(</span>one<span class="token punctuation">,</span> two<span class="token punctuation">,</span> three<span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token builtin">enumerate</span><span class="token punctuation">(</span><span class="token builtin">zip</span><span class="token punctuation">(</span>first<span class="token punctuation">,</span> second<span class="token punctuation">,</span> third<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">print</span><span class="token punctuation">(</span>count<span class="token punctuation">,</span> one<span class="token punctuation">,</span> two<span class="token punctuation">,</span> three<span class="token punctuation">)</span> <span class="token comment"># 0 a d g</span> <span class="token comment"># 1 b e h</span> <span class="token comment"># 2 c f i</span></code></pre></div> <h1 id="object-oriented-programming-oop" style="position:relative;"><a href="#object-oriented-programming-oop" aria-label="object oriented programming oop permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Object-oriented programming (OOP)</h1> <ul> <li><a href="https://realpython.com/python-is-identity-vs-equality/" target="_blank" rel="nofollow noopener noreferrer">Python ‘!=’ Is Not ‘is not’: Comparing Objects in Python</a></li> <li><a href="https://realpython.com/inheritance-composition-python/" target="_blank" rel="nofollow noopener noreferrer">Inheritance and Composition: A Python OOP Guide</a></li> <li><a href="https://realpython.com/python-super/" target="_blank" rel="nofollow noopener noreferrer">Supercharge Your Classes With Python super()</a></li> <li><a href="https://realpython.com/factory-method-python/" target="_blank" rel="nofollow noopener noreferrer">The Factory Method Pattern and Its Implementation in Python</a></li> </ul> <h1 id="other-topics" style="position:relative;"><a href="#other-topics" aria-label="other topics permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Other topics</h1> <ul> <li><a href="https://realpython.com/absolute-vs-relative-python-imports/" target="_blank" rel="nofollow noopener noreferrer">Absolute vs Relative Imports in Python</a></li> <li><a href="https://realpython.com/python-virtual-environments-a-primer/" target="_blank" rel="nofollow noopener noreferrer">Python Virtual Environments: A Primer</a></li> <li><a href="https://realpython.com/python-main-function/" target="_blank" rel="nofollow noopener noreferrer">Defining Main Functions in Python</a></li> <li><a href="https://realpython.com/python-continuous-integration/" target="_blank" rel="nofollow noopener noreferrer">Continuous Integration With Python: An Introduction</a></li> </ul> <h1 id="opening-file-streams" style="position:relative;"><a href="#opening-file-streams" aria-label="opening file streams permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Opening file streams</h1> <ul> <li><a href="https://stackoverflow.com/questions/3012488/what-is-the-python-with-statement-designed-for" target="_blank" rel="nofollow noopener noreferrer">What is the python “with” statement designed for?</a></li> </ul> <h1 id="practice-materials" style="position:relative;"><a href="#practice-materials" aria-label="practice materials permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Practice materials</h1> <ul> <li><a href="https://realpython.com/python-practice-problems/" target="_blank" rel="nofollow noopener noreferrer">Python Practice Problems: Get Ready for Your Next Interview</a></li> <li><a href="https://realpython.com/python-coding-interview-tips/" target="_blank" rel="nofollow noopener noreferrer">How to Stand Out in a Python Coding Interview</a></li> </ul> <h1 id="references" style="position:relative;"><a href="#references" aria-label="references permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>References</h1> <ul> <li><a href="https://realpython.com/python-pass-by-reference/" target="_blank" rel="nofollow noopener noreferrer">Pass by Reference in Python: Background and Best Practices</a></li> </ul> <h1 id="return-statements--generators" style="position:relative;"><a href="#return-statements--generators" aria-label="return statements generators permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Return statements &#x26; generators</h1> <ul> <li><a href="https://realpython.com/python-return-statement/" target="_blank" rel="nofollow noopener noreferrer">The Python return Statement: Usage and Best Practices</a></li> </ul> <h1 id="string-formatting" style="position:relative;"><a href="#string-formatting" aria-label="string formatting permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>String formatting</h1> <ul> <li><a href="https://realpython.com/python-string-formatting/" target="_blank" rel="nofollow noopener noreferrer">Python String Formatting Best Practices</a></li> </ul> <h4 id="old-style-string-formatting--operator" style="position:relative;"><a href="#old-style-string-formatting--operator" aria-label="old style string formatting operator permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Old Style” String Formatting (% Operator)</h4> <div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token string">'Hello, %s'</span> <span class="token operator">%</span> name <span class="token comment"># "Hello, Bob"</span> <span class="token string">'Hey %s, there is a 0x%x error!'</span> <span class="token operator">%</span> <span class="token punctuation">(</span>name<span class="token punctuation">,</span> errno<span class="token punctuation">)</span> <span class="token comment"># 'Hey Bob, there is a 0xbadc0ffee error!'</span> <span class="token string">'Hey %(name)s, there is a 0x%(errno)x error!'</span> <span class="token operator">%</span> <span class="token punctuation">{</span> <span class="token string">"name"</span><span class="token punctuation">:</span> name<span class="token punctuation">,</span> <span class="token string">"errno"</span><span class="token punctuation">:</span> errno <span class="token punctuation">}</span> <span class="token comment"># 'Hey Bob, there is a 0xbadc0ffee error!'</span></code></pre></div> <p>This makes your format strings easier to maintain and easier to modify in the future. You don’t have to worry about making sure the order you’re passing in the values matches up with the order in which the values are referenced in the format string. Of course, the downside is that this technique requires a little more typing.</p> <h4 id="new-style-string-formatting-strformat" style="position:relative;"><a href="#new-style-string-formatting-strformat" aria-label="new style string formatting strformat permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>“New Style” String Formatting (str.format)</h4> <div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token string">'Hello, {}'</span><span class="token punctuation">.</span><span class="token builtin">format</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span> <span class="token comment"># 'Hello, Bob'</span> <span class="token string">'Hey {name}, there is a 0x{errno:x} error!'</span><span class="token punctuation">.</span><span class="token builtin">format</span><span class="token punctuation">(</span>name<span class="token operator">=</span>name<span class="token punctuation">,</span> errno<span class="token operator">=</span>errno<span class="token punctuation">)</span> <span class="token comment"># 'Hey Bob, there is a 0xbadc0ffee error!'</span></code></pre></div> <p>This also shows that the syntax to format an int variable as a hexadecimal string has changed. Now you need to pass a format spec by adding a :x suffix. The format string syntax has become more powerful without complicating the simpler use cases.</p> <h4 id="string-interpolation--f-strings-python-36" style="position:relative;"><a href="#string-interpolation--f-strings-python-36" aria-label="string interpolation f strings python 36 permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>String Interpolation / f-Strings (Python 3.6+)</h4> <div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token string-interpolation"><span class="token string">f'Hello, </span><span class="token interpolation"><span class="token punctuation">{</span>name<span class="token punctuation">}</span></span><span class="token string">!'</span></span> <span class="token comment"># 'Hello, Bob!'</span> <span class="token string-interpolation"><span class="token string">f'Five plus ten is </span><span class="token interpolation"><span class="token punctuation">{</span>a <span class="token operator">+</span> b<span class="token punctuation">}</span></span><span class="token string"> and not </span><span class="token interpolation"><span class="token punctuation">{</span><span class="token number">2</span> <span class="token operator">*</span> <span class="token punctuation">(</span>a <span class="token operator">+</span> b<span class="token punctuation">)</span><span class="token punctuation">}</span></span><span class="token string">.'</span></span> <span class="token comment"># 'Five plus ten is 15 and not 30.'</span> <span class="token string-interpolation"><span class="token string">f"Hey </span><span class="token interpolation"><span class="token punctuation">{</span>name<span class="token punctuation">}</span></span><span class="token string">, there's a </span><span class="token interpolation"><span class="token punctuation">{</span>errno<span class="token punctuation">:</span><span class="token format-spec">#x</span><span class="token punctuation">}</span></span><span class="token string"> error!"</span></span> <span class="token comment"># "Hey Bob, there's a 0xbadc0ffee error!"</span></code></pre></div> <h4 id="template-strings-standard-library" style="position:relative;"><a href="#template-strings-standard-library" aria-label="template strings standard library permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Template Strings (Standard Library)</h4> <div class="gatsby-highlight" data-language="python"><pre class="language-python"><code class="language-python"><span class="token keyword">from</span> string <span class="token keyword">import</span> Template t <span class="token operator">=</span> Template<span class="token punctuation">(</span><span class="token string">'Hey, $name!'</span><span class="token punctuation">)</span> t<span class="token punctuation">.</span>substitute<span class="token punctuation">(</span>name<span class="token operator">=</span>name<span class="token punctuation">)</span> <span class="token comment"># 'Hey, Bob!'</span> templ_string <span class="token operator">=</span> <span class="token string">'Hey $name, there is a $error error!'</span> Template<span class="token punctuation">(</span>templ_string<span class="token punctuation">)</span><span class="token punctuation">.</span>substitute<span class="token punctuation">(</span>name<span class="token operator">=</span>name<span class="token punctuation">,</span> error<span class="token operator">=</span><span class="token builtin">hex</span><span class="token punctuation">(</span>errno<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment"># 'Hey Bob, there is a 0xbadc0ffee error!'</span> user_input <span class="token operator">=</span> <span class="token string">'${error.__init__.__globals__[SECRET]}'</span> Template<span class="token punctuation">(</span>user_input<span class="token punctuation">)</span><span class="token punctuation">.</span>substitute<span class="token punctuation">(</span>error<span class="token operator">=</span>err<span class="token punctuation">)</span> <span class="token comment"># ValueError:</span> <span class="token comment"># "Invalid placeholder in string: line 1, col 1"</span></code></pre></div> <h1 id="testing" style="position:relative;"><a href="#testing" aria-label="testing permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Testing</h1> <ul> <li><a href="https://realpython.com/python-testing/" target="_blank" rel="nofollow noopener noreferrer">Getting Started With Testing in Python</a></li> <li><a href="https://realpython.com/python-cli-testing/" target="_blank" rel="nofollow noopener noreferrer">4 Techniques for Testing Python Command-Line (CLI) Apps</a></li> </ul> <h1 id="threading" style="position:relative;"><a href="#threading" aria-label="threading permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Threading</h1> <ul> <li><a href="https://realpython.com/intro-to-python-threading/" target="_blank" rel="nofollow noopener noreferrer">An Intro to Threading in Python</a></li> <li><a href="https://realpython.com/python-concurrency/" target="_blank" rel="nofollow noopener noreferrer">Speed Up Your Python Program With Concurrency</a></li> </ul><![CDATA[Why dockerizing production isn't bad idea]]>https://sawczuk.dev/posts/why-dockerizing-production-isnt-bad-ideahttps://sawczuk.dev/posts/why-dockerizing-production-isnt-bad-ideaTue, 24 Nov 2020 19:39:40 GMT<p>The use of docker is a common technique in dev teams. But really, creating and using the complete ready-made image shows its potential in the production environment. I think this post will convince some Ops people to look more positively at running closed images over the installation and putting projects directly on the servers.</p> <p>There are many benefits to using images. For the development of the application, its tests, and automation, up to a running product. Along with the development of the industry, there were also many new technologies enabling the management of more servers as part of the application publishing itself. Some of them were designed directly by the current cloud operators. The emergence of a new branch of technology also forced the creation of a new profession - DevOps.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/0406950420859870f33ed83b1e3fe583/47311/server-6.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBP/EABYBAQEBAAAAAAAAAAAAAAAAAAABAv/aAAwDAQACEAMQAAABytPCSxf/xAAaEAACAgMAAAAAAAAAAAAAAAAAARFBEiEy/9oACAEBAAEFAp1eDGWuP//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/AUf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAdEAABAgcAAAAAAAAAAAAAAAAAASECEBExQXGB/9oACAEBAAY/AncvQzySEOj/xAAfEAACAQIHAAAAAAAAAAAAAAABEQAhURAxQXGBodH/2gAIAQEAAT8heodp9lEJdYy6j0jKOYO44H//2gAMAwEAAgADAAAAEJvf/8QAFhEAAwAAAAAAAAAAAAAAAAAAEBFB/9oACAEDAQE/EKg//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHxABAQABAwUBAAAAAAAAAAAAAREAITFREEGBocGx/9oACAEBAAE/EFjEsAicQa3EU5QNk5j937YBWhWCh5DXPa+OhvTfrn//2Q=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/0406950420859870f33ed83b1e3fe583/8ac56/server-6.webp 240w, /static/0406950420859870f33ed83b1e3fe583/d3be9/server-6.webp 480w, /static/0406950420859870f33ed83b1e3fe583/e46b2/server-6.webp 960w, /static/0406950420859870f33ed83b1e3fe583/260c2/server-6.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/0406950420859870f33ed83b1e3fe583/09b79/server-6.jpg 240w, /static/0406950420859870f33ed83b1e3fe583/7cc5e/server-6.jpg 480w, /static/0406950420859870f33ed83b1e3fe583/6a068/server-6.jpg 960w, /static/0406950420859870f33ed83b1e3fe583/47311/server-6.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/0406950420859870f33ed83b1e3fe583/6a068/server-6.jpg" alt="Birth of DevOps" title="Birth of DevOps" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>Each team looks for Docker and its derivative benefits in terms of work efficiency and simplification of the tasks assigned to them. Therefore, to better understand each of the teams and their point of view, each has its chapter. This will allow you to understand the possibility of using this technology in every step of the project.</p> <h2 id="dev" style="position:relative;"><a href="#dev" aria-label="dev permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Dev</h2> <p>Creating docker images while developing an application is currently one of the requirements in the hiring process. This is more or less because previously programmers had to build up their programming environment. Knowledge of Linux was a standard in the job description ever to know how to install the appropriate version of packages for the operation of their project. Unfortunately, the problem of installing main packages by programmers grew when they started working on several projects at the same time in different global package versions. Unfortunately, switching between versions was not convenient, it often required additional configuration files, and sometimes it forced to re-install them. Virtualization came to the rescue, which separated each project and did not require having specific versions of packages on the user’s main system. Due to the <a href="https://geekflare.com/docker-vs-virtual-machine/" target="_blank" rel="nofollow noopener noreferrer">hardware consumption and number of layers</a> in virtualization technology, Docker is the most used today. The development team needs to use application images because the same versions of the libraries are used on each workstation. To make editing the project code comfortable, it is common practice to link the folder with the code from the user’s machine to the running image. This operation makes the environment clean enough to allow the project to be built from scratch as if it were in a regular non-image environment. This allows for the utmost control over the project.</p> <h2 id="devops" style="position:relative;"><a href="#devops" aria-label="devops permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>DevOps</h2> <p>This position is mostly related to the CI/CD function. The absolute automation of the application release process, updating images for security reasons, and control of application communication is what characterizes this work. With more projects to look after, the job requires a lot of management procedures. Recently, due to the growing interest among others in <a href="https://kubernetes.io/" target="_blank" rel="nofollow noopener noreferrer">Kubernetes</a>, DevOps is more and more often dealing with new tools offered by operators like Google Cloud or AWS. The arrangement of procedures in this profession is very important. Thanks to this, work with applications can be automated and images properly prepared. These people look after the internal environment of the application, test performance, check application statistics, and try to improve every point of it. The variation of tools for DevOps is huge, and they are often mixed - library changes can be compared to the same dynamically changing frontend technology. The Best description? One command should execute and handle each exception. Obtaining total automation in a project is the greatest reward.</p> <h2 id="ops" style="position:relative;"><a href="#ops" aria-label="ops permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Ops</h2> <p>The main task of this team is to create a safe system. Protection of any undesirable entrances, provision of external services, and monitoring the SLA of servers. Due to the profile of this profession, it is heavily responsible for data access and security. Increasingly, companies are moving away from burdening this team with app releases (due to the possibility of unexpected errors during updates, restoring the previous version, and consulting with the development team). Since these activities were a shared responsibility of the teams and usually required additional access for other departments, this function is postponed for safety. Instead, these actions are now being replaced with images of the application. The difference between the old and the new way can be broken down into points.</p> <p>Classic prod environment update (application downtime):</p> <ul> <li>blocking access to the application as part of updating the environment</li> <li>[optional] software update to support new functionalities (~ 10 min)</li> <li>updating application libraries (~ 5min)</li> <li>[optional] migrations in the database (~ 5min)</li> <li>unblocking access and admitting users</li> <li>negative path</li> <li>restoring the old code version (~ 1 min)</li> <li>restoration of the old software version (if the functions were deprecated) (~ 10 min)</li> <li>installation of application libraries (~ 5 min)</li> </ul> <p>Updating the prod environment using Docker tools (application downtime):</p> <ul> <li>download an image with the code included</li> <li>replacing the version of the prepared image and restarting the configuration</li> <li>blocking access to the application as part of updating the environment</li> <li>docker facilitates this by using load balancing, which makes this point optimal</li> <li>in the image, you can add code that will do some tasks before starting the motion in the application</li> <li>[optional] database migrations (~ 2-5min)</li> <li>when starting the image, check if you should perform the migration, if not - continue</li> <li>negative path</li> <li>run old version of the image (~ 1 min)</li> </ul> <p>Seeing the steps given, you can see that preparing an image with built libraries, a good version of the software, and included code is a big profit. By adding release automation and even server conversion to a <a href="https://kubernetes.io/docs/concepts/overview/what-is-kubernetes/" target="_blank" rel="nofollow noopener noreferrer">Kubernetes cluster</a>, we simplify the issue of publishing drastically. Images created by teams are closed by default and it is up to us how we make them available to the world - or the environment configuration team will take care of it. By receiving such a simplification, people responsible for security and services can focus on their tickets and improving their work.</p><![CDATA[Knowledge books: Photography inspirations and cheat sheets]]>https://sawczuk.dev/posts/knowledge-books-photography-inspirations-and-cheat-sheetshttps://sawczuk.dev/posts/knowledge-books-photography-inspirations-and-cheat-sheetsMon, 16 Nov 2020 00:04:08 GMT<p><em><strong>Knowledge books</strong> is a mini series of constantly updated articles in which I share my more interesting discoveries</em></p> <p>It happens that in the photos with the model we have no idea for new frames, or the ones that were used are repeated in each session. When looking for alternatives of styles, poses, arrangement of light or the use of objects in web, we often come across links that are not needed at the moment. However, it is worth saving them for later and grouping them.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/2bba6e2b0f6f812deac9d3bdeec2f6ce/47311/photo-2.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAQFA//EABQBAQAAAAAAAAAAAAAAAAAAAAP/2gAMAwEAAhADEAAAAZy9eYSrmgh//8QAGRAAAgMBAAAAAAAAAAAAAAAAAAECAxIz/9oACAEBAAEFAlCSeWMsLeh//8QAFhEBAQEAAAAAAAAAAAAAAAAAAhAR/9oACAEDAQE/AUcn/8QAFhEBAQEAAAAAAAAAAAAAAAAAAhAR/9oACAECAQE/AStn/8QAGRAAAgMBAAAAAAAAAAAAAAAAAQIAEBFx/9oACAEBAAY/AsYQim5Gr//EABwQAAICAgMAAAAAAAAAAAAAAAARITEBEEFRof/aAAgBAQABPyFxUcCHOLMJ7KH6tf/aAAwDAQACAAMAAAAQMw//xAAWEQEBAQAAAAAAAAAAAAAAAAAAARH/2gAIAQMBAT8QhLH/xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAgEBPxBnP//EAB0QAQADAAEFAAAAAAAAAAAAAAEAESGhMUFxgbH/2gAIAQEAAT8Q2gnKdPL7lkGLgukyJQ0AUhOa+k56dif/2Q=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/2bba6e2b0f6f812deac9d3bdeec2f6ce/8ac56/photo-2.webp 240w, /static/2bba6e2b0f6f812deac9d3bdeec2f6ce/d3be9/photo-2.webp 480w, /static/2bba6e2b0f6f812deac9d3bdeec2f6ce/e46b2/photo-2.webp 960w, /static/2bba6e2b0f6f812deac9d3bdeec2f6ce/260c2/photo-2.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/2bba6e2b0f6f812deac9d3bdeec2f6ce/09b79/photo-2.jpg 240w, /static/2bba6e2b0f6f812deac9d3bdeec2f6ce/7cc5e/photo-2.jpg 480w, /static/2bba6e2b0f6f812deac9d3bdeec2f6ce/6a068/photo-2.jpg 960w, /static/2bba6e2b0f6f812deac9d3bdeec2f6ce/47311/photo-2.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/2bba6e2b0f6f812deac9d3bdeec2f6ce/6a068/photo-2.jpg" alt="Photography: inspirations and cheat sheets" title="Photography: inspirations and cheat sheets" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <h2 id="pinterest" style="position:relative;"><a href="#pinterest" aria-label="pinterest permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Pinterest</h2> <p>Every now and then, it’s a good idea to sit down for a day and gather ideas. We can then save the collected photos or links in specific styles and present them to other people. Thanks to a well-prepared modboard, which looks different depending on the person who uses it, we can predict in what style a particular session would be aimed at. <a href="https://pinterest.com" target="_blank" rel="nofollow noopener noreferrer">Signing up is free</a> and collecting ideas is really interesting. From one day and looking for similar photos, we can choose a lot of styles and inspirations.</p> <p>Some of my boards:</p> <ul> <li><a href="https://pinterest.com/atryni/psboard-nature/" target="_blank" rel="nofollow noopener noreferrer">Nature (All seasons)</a></li> <li><a href="https://pinterest.com/atryni/psboard-urban/" target="_blank" rel="nofollow noopener noreferrer">Urban</a></li> <li><a href="https://pinterest.com/atryni/psboard-outside-night/" target="_blank" rel="nofollow noopener noreferrer">Outside in night</a></li> <li><a href="https://pinterest.com/atryni/psboard-fashion/" target="_blank" rel="nofollow noopener noreferrer">Fashion/Studio background</a></li> <li><a href="https://pinterest.com/atryni/psboard-comfy/" target="_blank" rel="nofollow noopener noreferrer">Comfy</a></li> <li><a href="https://pinterest.com/atryni/psboard-boudoir/" target="_blank" rel="nofollow noopener noreferrer">Boudoir</a></li> <li><a href="https://pinterest.com/atryni/psboard-sensual/" target="_blank" rel="nofollow noopener noreferrer">Sensual</a></li> <li><a href="https://pinterest.com/atryni/psboard-christmas/" target="_blank" rel="nofollow noopener noreferrer">Santa/Christmas</a></li> <li><a href="https://pinterest.com/atryni/psboard-chair/" target="_blank" rel="nofollow noopener noreferrer">Interesting poses with the use of a chair</a></li> <li><a href="https://pinterest.com/atryni/psboard-poses/" target="_blank" rel="nofollow noopener noreferrer">Poses for models</a></li> <li><a href="https://pl.pinterest.com/atryni/psboard-light-setup/" target="_blank" rel="nofollow noopener noreferrer">Light setup</a></li> <li><a href="https://pinterest.com/atryni/psboard-to-sort/" target="_blank" rel="nofollow noopener noreferrer">New ideas to sort in right boards</a></li> </ul> <h2 id="poses" style="position:relative;"><a href="#poses" aria-label="poses permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Poses</h2> <p>Missing poses for photos in the background can be supplemented using materials for artists. They often use real photos or the person themselves. Using the resources of, among others, the <em>Art Model Reference Images to Draw</em> series created by New Masters Academy, with each episode we can be inspired by reference photos</p> <ul> <li><a href="https://www.youtube.com/watch?v=rWbeelH9eCA" target="_blank" rel="nofollow noopener noreferrer">Art Model Reference - Ballet</a></li> <li><a href="https://www.youtube.com/watch?v=mNNSwITdPIM" target="_blank" rel="nofollow noopener noreferrer">Art Model Reference - Athletics</a></li> <li><a href="https://www.youtube.com/watch?v=tPLRJrlq7x0" target="_blank" rel="nofollow noopener noreferrer">Art Model Reference - Male poses</a></li> <li><a href="https://www.youtube.com/watch?v=a7gmb1Ba8WA" target="_blank" rel="nofollow noopener noreferrer">Art Model Reference - Male poses on black background</a></li> <li><a href="https://www.youtube.com/watch?v=hXVnwARYmqo" target="_blank" rel="nofollow noopener noreferrer">Art Model Reference - Sensual poses on black background</a></li> </ul> <h2 id="lighting-setup" style="position:relative;"><a href="#lighting-setup" aria-label="lighting setup permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Lighting setup</h2> <ul> <li><a href="https://www.youtube.com/watch?v=Z4eVEmcBwxk" target="_blank" rel="nofollow noopener noreferrer">Tips from Michael Muller</a></li> <li><a href="https://www.youtube.com/watch?v=JIcV06atCPY" target="_blank" rel="nofollow noopener noreferrer">Use of Continuous Lighting for Food Photography</a></li> <li><a href="https://youtu.be/2QHe1SRoTDQ" target="_blank" rel="nofollow noopener noreferrer">Single Light vs Multiple Light</a></li> <li><a href="https://youtu.be/L8Zd_jFJvqs" target="_blank" rel="nofollow noopener noreferrer">ONE LIGHT Flash Portrait Photography Setup</a></li> <li><a href="https://youtu.be/L199IpvSsRI" target="_blank" rel="nofollow noopener noreferrer">2 Portrait Lighting Setups For Men</a></li> <li><a href="https://youtu.be/2MyvV1BQmps" target="_blank" rel="nofollow noopener noreferrer">Add Color Gels To Your Photography Lighting</a></li> <li><a href="https://www.youtube.com/watch?v=8ZFWLOl-TiE" target="_blank" rel="nofollow noopener noreferrer">How To Use Natural Light for Boudoir Portrait Photography</a></li> <li><a href="https://www.youtube.com/watch?v=-z2bu-TT14s" target="_blank" rel="nofollow noopener noreferrer">DRAMATIC Portraits with SIMPLE LED Lights</a></li> <li><a href="https://www.youtube.com/watch?v=zfvxpL5s_KA" target="_blank" rel="nofollow noopener noreferrer">Embrace your Shadows: How to Light and Shoot for Dark Food Photography</a></li> <li><a href="https://www.youtube.com/watch?v=iiUTTk67kXE" target="_blank" rel="nofollow noopener noreferrer">10ish Cinematic Lighting Setups… ONLY ONE LIGHT</a></li> <li><a href="https://www.youtube.com/watch?v=WKpj-QyUKOk" target="_blank" rel="nofollow noopener noreferrer">Mastering one-light images with Felix Kunze</a></li> <li><a href="https://www.youtube.com/watch?v=Bb0aBun182g" target="_blank" rel="nofollow noopener noreferrer">Beauty dish vs Softbox vs Umbrella | Light Modifiers Compared</a></li> <li><a href="https://www.youtube.com/watch?v=ohFOLUF1NNM" target="_blank" rel="nofollow noopener noreferrer">Can you use a SPEEDLIGHT to take dramatic portraits in bright sun?</a></li> <li><a href="https://www.youtube.com/watch?v=uMwGVYU_eq0" target="_blank" rel="nofollow noopener noreferrer">The ULTIMATE 3 Light setup in 150 seconds!</a></li> <li><a href="https://www.youtube.com/watch?v=jr-5Q2LDafg" target="_blank" rel="nofollow noopener noreferrer">The ULTIMATE one Light setup on a BUDGET (Anyone can do this)</a></li> </ul> <h2 id="behind-the-scenes" style="position:relative;"><a href="#behind-the-scenes" aria-label="behind the scenes permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Behind the scenes</h2> <ul> <li><a href="https://youtu.be/iuXiTtFOO20Golden" target="_blank" rel="nofollow noopener noreferrer">Hour Beach Photoshoot with Irene Rudnyk</a></li> <li><a href="https://youtu.be/6_l2dBFJscw" target="_blank" rel="nofollow noopener noreferrer">Natural Light Backlit Photoshoot with Irene Rudnyk</a></li> <li><a href="https://youtu.be/w9b07-9aDZs" target="_blank" rel="nofollow noopener noreferrer">4 PHOTOGRAPHERS SHOOT THE SAME MODEL</a></li> <li><a href="https://youtu.be/tQHHsXgzywg" target="_blank" rel="nofollow noopener noreferrer">Dramatic Lighting and Smoke</a></li> </ul> <h2 id="tips" style="position:relative;"><a href="#tips" aria-label="tips permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Tips</h2> <ul> <li><a href="https://www.youtube.com/watch?v=BbySBYjL4E0" target="_blank" rel="nofollow noopener noreferrer">How To Learn Boudoir Photography Fast</a></li> <li><a href="https://www.youtube.com/watch?v=UIP3U05ZPgQ" target="_blank" rel="nofollow noopener noreferrer">How To Get VARIETY In Your Boudoir Photos</a></li> <li><a href="https://www.youtube.com/watch?v=Pfyq8z0TpQc" target="_blank" rel="nofollow noopener noreferrer">120 seconds of Studio Photography tips</a></li> <li><a href="https://www.youtube.com/watch?v=eqdvNaLarz4" target="_blank" rel="nofollow noopener noreferrer">HSS Flash in the Studio</a></li> <li><a href="https://www.youtube.com/watch?v=uay4SpzT2wc" target="_blank" rel="nofollow noopener noreferrer">Flash Sync Speed in the Studio</a></li> </ul><![CDATA[Knowledge books: Food recipes]]>https://sawczuk.dev/posts/knowledge-books-food-recipeshttps://sawczuk.dev/posts/knowledge-books-food-recipesSun, 15 Nov 2020 19:08:58 GMT<p><em><strong>Knowledge books</strong> is a mini series of constantly updated articles in which I share my more interesting discoveries</em></p> <p>Free time is a really good thing. We can use it freely - including cooking something extraordinary. In this time we can approach cooking with great creativity and eagerness. Having the recipe, we can modify it at will, there are no bad results. When finding a good new recipe, I will certainly keep the list updated. In the end, I will also look here myself to recall even the list of ingredients or the next steps.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/782cc42a9b7c691eb2e8f97573884aa0/47311/food-2.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAABAABA//EABYBAQEBAAAAAAAAAAAAAAAAAAIBA//aAAwDAQACEAMQAAABN1MnJntqv//EABoQAAICAwAAAAAAAAAAAAAAAAECADEDEBH/2gAIAQEAAQUCydDRrOjf/8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQMBAT8BJ//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABYQAAMAAAAAAAAAAAAAAAAAABESIP/aAAgBAQAGPwJjX//EABoQAQEAAgMAAAAAAAAAAAAAAAEAEDFBUYH/2gAIAQEAAT8hK0Vei9hdCeMX/9oADAMBAAIAAwAAABBc7//EABYRAQEBAAAAAAAAAAAAAAAAAAARMf/aAAgBAwEBPxDaP//EABYRAQEBAAAAAAAAAAAAAAAAAAABEf/aAAgBAgEBPxCNf//EAB4QAQACAQQDAAAAAAAAAAAAAAEAESExUXGxocHx/9oACAEBAAE/EHyW5ZrY4qUgDZxafZnep5D0ToJrz//Z'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/782cc42a9b7c691eb2e8f97573884aa0/8ac56/food-2.webp 240w, /static/782cc42a9b7c691eb2e8f97573884aa0/d3be9/food-2.webp 480w, /static/782cc42a9b7c691eb2e8f97573884aa0/e46b2/food-2.webp 960w, /static/782cc42a9b7c691eb2e8f97573884aa0/260c2/food-2.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/782cc42a9b7c691eb2e8f97573884aa0/09b79/food-2.jpg 240w, /static/782cc42a9b7c691eb2e8f97573884aa0/7cc5e/food-2.jpg 480w, /static/782cc42a9b7c691eb2e8f97573884aa0/6a068/food-2.jpg 960w, /static/782cc42a9b7c691eb2e8f97573884aa0/47311/food-2.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/782cc42a9b7c691eb2e8f97573884aa0/6a068/food-2.jpg" alt="Lets go cooking" title="Lets go cooking" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <h2 id="chaliapin-steak-don" style="position:relative;"><a href="#chaliapin-steak-don" aria-label="chaliapin steak don permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Chaliapin Steak Don</h2> <p>The recipe is obtained mainly from the Food Wars anime, which beautifully describes and imitates real recipes known all over the world. I had several approaches to this dish and I must admit, there are no bad results here - each variant tastes special. Don himself is really well described on the <a href="https://www.youtube.com/watch?v=6TiCcI3s6ZM" target="_blank" rel="nofollow noopener noreferrer">CHEFPK channel</a>.</p> <p>Ingredients:</p> <ul> <li>beef meat</li> <li>1 onion / 200g meat</li> <li>rice</li> <li>peper &#x26; salt</li> </ul> <p>Extras:</p> <ul> <li>maple syrup + miso for marinate</li> <li>Japanese prunes or other bitter dried fruit</li> </ul> <p>As a first step, we prepare the onion by cutting it into cubes. Put the first layer on the plate, then put the meat in it and cover it with the second layer of onion. The beef should then wait in this state for half to two hours. While waiting for the taste to soak and soften the meat, we can prepare the rice. After that time, remove the onion from meat and sauté it in a pan. Burn the meat in a frying pan and prepare it for the frying you want. The last step is to put rice, meat and a hill of onion into the bowl and pour the resulting sauce from the pan over it.</p> <p>Modifications:</p> <ul> <li>The meat can be prepared sous vide by enclosing it in a bag together with the marinade</li> <li>An addition to rice can be rice sauce or dried Japanese cherry particles</li> <li>While in a rice cooker, you can add additional ingredients to make rice soak in additional flavors</li> </ul> <h2 id="massaman-curry" style="position:relative;"><a href="#massaman-curry" aria-label="massaman curry permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Massaman curry</h2> <p>It’s worth experimenting in the kitchen. Thanks to them, we have rediscovery tasteful combinations of flavors. Last time I was able to put together a good combination for the curry</p> <p>Ingredients:</p> <ul> <li>chicken/pork/beef meat</li> <li>1 onion / 200g meat</li> <li>massaman curry paste</li> <li>rice</li> <li>soy sauce &#x26; teriyaki sauce</li> <li>salt, garlic, citric acid, ground paprika, ginger</li> </ul> <p>Extras:</p> <ul> <li>yakisoba sauce</li> <li>dry wine </li> <li>rice vinegar</li> </ul> <p>Preparations should start with the onion glazing and rice cooking simultaneously. During frying, it is worth adding spices such as garlic, citric acid, and a pinch of salt to it. When the onion is almost ready, add the meat that will use the current sauce. To increase the flavor variation of the meat, you can add additional spices such as ground paprika or ginger. The next step is to pour coconut milk over the pan content and add the curry paste. Add soy and teriyaki sauce, rice vinegar, and reduce the sauce. Halfway through the process, add extra dry wine to enhance the umami of the curry. The finished product should be served in a bowl along with rice, cherry tomatoes, and chives. The variation in the preparation of the dish may simply change.</p> <p>Modifications:</p> <ul> <li> <p>a flavor variation to the cooking rice</p> <ul> <li>yakisoba sauce and rice vinegar</li> <li>soy sauce and a mixture of ground paprika</li> </ul> </li> <li>curry works very well with the 5-taste variant (not 5-flavor variant), so add a different mix of spices</li> <li>rice to increase its taste, you can fry it slowly with an egg in a pan (it’s worth adding spices to close them in the middle of the rice coated with egg protein)</li> </ul><![CDATA[Know what beef you want to add to the basket]]>https://sawczuk.dev/posts/know-what-beef-you-want-to-add-to-the-baskethttps://sawczuk.dev/posts/know-what-beef-you-want-to-add-to-the-basketSun, 15 Nov 2020 18:00:50 GMT<p>Buying good meat is really important. It may turn out that the meat seen on the promotion or on the shelf may not completely match our dish. Therefore, it makes sense to have a list of pieces of meat that are suitable for specific dishes. It is also worth being aware of what the meat should look like or what price you can expect.</p> <p>I make a list of all these types after I bought a weaker quality entrecote, which turned out to be stringy at the very end. The dish was saved, but the steaks turned into a stew. It is worth considering whether the quality and price of the meat will not affect the taste of the dish. Unfortunately, it did not work after a small thermal treatment today.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/f7baef5be50f98d274d7ae2127a63a14/47311/food-3.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABAUA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAwT/2gAMAwEAAhADEAAAAZ9CKkKG5unT/8QAGxAAAgEFAAAAAAAAAAAAAAAAAQIAAxITMkL/2gAIAQEAAQUCo2RlBRcoCbdmf//EABgRAAIDAAAAAAAAAAAAAAAAAAABAhIh/9oACAEDAQE/AYaiqP/EABgRAAIDAAAAAAAAAAAAAAAAAAACARMh/9oACAECAQE/AXXSyT//xAAaEAACAgMAAAAAAAAAAAAAAAAAARAhETEy/9oACAEBAAY/ArVnWGtQhR//xAAcEAEAAgEFAAAAAAAAAAAAAAABABFBECFRcYH/2gAIAQEAAT8haKA5QuOSsFDb3TMsep//2gAMAwEAAgADAAAAEMAv/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAERIf/aAAgBAwEBPxCdUIP/xAAWEQEBAQAAAAAAAAAAAAAAAAABEDH/2gAIAQIBAT8QTLH/xAAbEAADAAIDAAAAAAAAAAAAAAAAARFBoWGx0f/aAAgBAQABPxBezY2SvZeKl/VJ8WbRtI65/9k='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/f7baef5be50f98d274d7ae2127a63a14/8ac56/food-3.webp 240w, /static/f7baef5be50f98d274d7ae2127a63a14/d3be9/food-3.webp 480w, /static/f7baef5be50f98d274d7ae2127a63a14/e46b2/food-3.webp 960w, /static/f7baef5be50f98d274d7ae2127a63a14/260c2/food-3.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/f7baef5be50f98d274d7ae2127a63a14/09b79/food-3.jpg 240w, /static/f7baef5be50f98d274d7ae2127a63a14/7cc5e/food-3.jpg 480w, /static/f7baef5be50f98d274d7ae2127a63a14/6a068/food-3.jpg 960w, /static/f7baef5be50f98d274d7ae2127a63a14/47311/food-3.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/f7baef5be50f98d274d7ae2127a63a14/6a068/food-3.jpg" alt="Choose right beef for you" title="Choose right beef for you" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <h3 id="entrecote-cote-de-boeuf" style="position:relative;"><a href="#entrecote-cote-de-boeuf" aria-label="entrecote cote de boeuf permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Entrecote (cote de boeuf)</h3> <p>It contains part of the longest back muscle and part of the intercostal muscles with very fine fibers. Additionally, it has beautiful hypertrophy of the so-called intramuscular fat. marble that makes the steaks particularly juicy.</p> <ul> <li>26.50€ / kg</li> <li>slices about 3cm thick</li> </ul> <h3 id="rib-eye" style="position:relative;"><a href="#rib-eye" aria-label="rib eye permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Rib Eye</h3> <p>The heart of the entrecote, after it is seasoned, the bone and the so-called outer sheath. rib eye is a slice of rib eye steak. It has a high degree of marbling and is therefore extremely delicate, full of flavor and aroma.</p> <ul> <li>42.00€ / kg</li> <li>slices about 3cm thick</li> </ul> <h3 id="tbone" style="position:relative;"><a href="#tbone" aria-label="tbone permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>TBone</h3> <p>King of steaks, adored by beef fans. It is the most popular steak in the US and England where it has become a legend. It owes its name to the characteristic T-shaped bone that divides the steak into 2 unequal parts. The smaller part is the tenderloin and the larger part is the roast beef. It requires careful grilling not to drag the part with the tenderloin. T-bone therefore has two flavors, roast beef and sirloin.</p> <ul> <li>31.00€ / kg</li> <li>slices about 3cm thick</li> </ul> <h3 id="porterhouse" style="position:relative;"><a href="#porterhouse" aria-label="porterhouse permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Porterhouse</h3> <p>The steak is cut in a very similar way to a t-bon but much bigger than it. Porterhouse steak is cut in the place where the tenderloin is thickest. A portion of Porterhouse steak weighs about 650 g and should therefore be fried 6-8 minutes on each side. Most often served as a dish for 2 people, cut into smaller slices, cut from the bones. When frying, remember that sirloin comes much faster than roast beef and should not be too long.</p> <ul> <li>35.50€ / kg</li> <li>slices about 3cm thick</li> </ul> <h3 id="new-york-roast-beef" style="position:relative;"><a href="#new-york-roast-beef" aria-label="new york roast beef permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>New York (roast beef)</h3> <p>A steak obtained from a part of the roast beef, with a great taste and meat structure. to preserve its full flavor, we cut it out with the bone. Considered by many experts as the tastiest Hereford steak.</p> <ul> <li>24.00€ / kg</li> <li>slices about 3cm thick</li> </ul> <h3 id="sirloin-rump-steak" style="position:relative;"><a href="#sirloin-rump-steak" aria-label="sirloin rump steak permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Sirloin (rump steak)</h3> <p>One of the most valuable types of beef for universal use, incl. in appetizers, soups, main courses as well as in snacks. Cut from cruciforms, they are slices slightly thinner than steaks that can be prepared on a grill or pan. A rump steak, stewed in a pan with caramelized onions and a delicate own sauce. </p> <ul> <li>15.50€ / kg</li> <li>slices about 1.5cm thick</li> </ul> <h3 id="ligava" style="position:relative;"><a href="#ligava" aria-label="ligava permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Ligava</h3> <p>Part of a beef leg - a strong, lean muscle. It is sometimes referred to as “false sirloin”. It is well suited for cooking and stewing. Ligawa steaks should not be overcooked as they can get dry and unpalatable.</p> <ul> <li>11.00€ / kg</li> <li>slices about 1.5cm thick</li> </ul><![CDATA[Does the beginning of learning programming always have to be boring?]]>https://sawczuk.dev/posts/learning-programming-not-always-have-to-be-boringhttps://sawczuk.dev/posts/learning-programming-not-always-have-to-be-boringMon, 09 Nov 2020 00:31:12 GMT<p>The beginnings are always difficult, whether when it comes to learning only programming or trying to learn a new language. Usually we are looking for a fresh idea to systematically develop it as part of the acquired knowledge. It’s hard to find something like this on the internet recently, so I’ll share my ideas.</p> <p>The basis of each project is its future development. We can assume that our application, project or script has a chance for development, but also a change of direction. Therefore, the basic project itself must be quite flexible when it comes to introducing changes and allow for the implementation of many improvements and test solutions. This is very important, especially when developing your skills in a free way, where we should have our sandbox at our disposal.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/36dd03a965c6b811e62deeb3963dd39e/47311/notes-2.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAQGAwX/xAAWAQEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAduhO0TS4wR//8QAGxAAAQQDAAAAAAAAAAAAAAAAAgEDBDEQETL/2gAIAQEAAQUCjO4VrajZclf/xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAwEBPwFn/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGhAAAgIDAAAAAAAAAAAAAAAAAAEQIRESkf/aAAgBAQAGPwLVllY4Kf/EABwQAQABBAMAAAAAAAAAAAAAAAEAECExQRFRof/aAAgBAQABPyEMAc4SOHXculp7j0FS/9oADAMBAAIAAwAAABDgH//EABcRAQEBAQAAAAAAAAAAAAAAAAEAEUH/2gAIAQMBAT8QHYXL/8QAFREBAQAAAAAAAAAAAAAAAAAAAQD/2gAIAQIBAT8QJC//xAAdEAACAgMAAwAAAAAAAAAAAAABIQARMVGxccHw/9oACAEBAAE/EAlslKejKuYoaqXlVwDxPlbmLwOicfc//9k='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/36dd03a965c6b811e62deeb3963dd39e/8ac56/notes-2.webp 240w, /static/36dd03a965c6b811e62deeb3963dd39e/d3be9/notes-2.webp 480w, /static/36dd03a965c6b811e62deeb3963dd39e/e46b2/notes-2.webp 960w, /static/36dd03a965c6b811e62deeb3963dd39e/260c2/notes-2.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/36dd03a965c6b811e62deeb3963dd39e/09b79/notes-2.jpg 240w, /static/36dd03a965c6b811e62deeb3963dd39e/7cc5e/notes-2.jpg 480w, /static/36dd03a965c6b811e62deeb3963dd39e/6a068/notes-2.jpg 960w, /static/36dd03a965c6b811e62deeb3963dd39e/47311/notes-2.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/36dd03a965c6b811e62deeb3963dd39e/6a068/notes-2.jpg" alt="Finding flexible base project" title="Finding flexible base project" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>The first thing that comes to my mind when it comes to use is building chatbots. By browsing bot repositories for Discord or Telegram, we can discover that there are really thousands of them. Why am I giving this idea first? I once created a bot repository for Telegram as part of a project with friends “Let’s write some cool code together”. Each person had a different idea, but this did not prevent us from implementing and developing the project. Each of us has evolved while writing code, team findings and understanding some procedures from inside out. Of course, if I come up with another project idea, I’d love to update it here.</p> <h2 id="flexibility" style="position:relative;"><a href="#flexibility" aria-label="flexibility permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Flexibility</h2> <p>Flexibility is the core of a design. By putting up a bot idea as the first, we can attach a development list to it. I will try to list the possibilities of project development divided into categories from the simplest to the more advanced. Of course, some categories presented may use similar solutions and tools. However, the point of looking at them changes here, and our knowledge, going through the previous points, can significantly improve our work on a more advanced topic. I will use python mentions in the list as now I am in the development stage of the language myself. Each of these solutions can also be used in a different language, and libraries will find alternatives. So no worries, although I think the references may bring some people closer to the subject than just a dry description.</p> <h2 id="bot-for-discord--telegram--other-communicator" style="position:relative;"><a href="#bot-for-discord--telegram--other-communicator" aria-label="bot for discord telegram other communicator permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Bot for Discord / Telegram / Other communicator</h2> <h4 id="readwrite-data" style="position:relative;"><a href="#readwrite-data" aria-label="readwrite data permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Read/write data</h4> <p>Possibility to create a list for a channel or for a user. Using commands we can add, subtract or edit items. So we need to save and later use this data somehow.</p> <ul> <li>Writing data to a file?</li> <li>How to get to them, edit, save?</li> <li>Maybe a database? Setting up the entire database server instance or are there any libraries to use local database option?</li> </ul> <p>Alternatively, maybe we can store some record of users who wrote in the chat, make a ranking who was active for how many days, an activity ranking for how many days in a row they were online.</p> <h4 id="schedule-tasks" style="position:relative;"><a href="#schedule-tasks" aria-label="schedule tasks permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Schedule tasks</h4> <p>The ability to run an operation at any time without the need to manually trigger an action. This is a really great option if we need to run some actions recurrently. The basic action could be notification of the collected data since the last time or sending a reminder. The activities for this module may increase with the next steps.</p> <ul> <li>Are there any libraries to run this kind of functions?</li> </ul> <h4 id="scrappingfetching--parsing" style="position:relative;"><a href="#scrappingfetching--parsing" aria-label="scrappingfetching parsing permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Scrapping/fetching + parsing</h4> <p>Let’s read an RSS feed and send info about new posts. We just need to know which posts are new and which are already scrapped. The RSS feed can be long, we do not want it to give us a list of all posts every 15 minutes. It would be great if scrapping could work automatically in the background, so there will be no need to run the command manually in the command. RSS feeds are often saved in XML, so you probably need to convert this data from text to some kind of list or dict - somehow program have to read it.</p> <h4 id="web--api" style="position:relative;"><a href="#web--api" aria-label="web api permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Web / API:</h4> <p>After some data saved, we can get to them via API? It’s only here, but for Python projects i strongly suggest using Django or fastAPI</p> <h4 id="test-your-code" style="position:relative;"><a href="#test-your-code" aria-label="test your code permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Test your code</h4> <p>Are you sure your code is 100% working? Are we getting good function results? Will the introduction of a new functionality break another part of the system, do all services communicate? We can answer all these questions by writing tests of our code. Thanks to this, we can rest easy and know that our changes have not damaged the integrity of the code.</p> <h4 id="documentation" style="position:relative;"><a href="#documentation" aria-label="documentation permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Documentation</h4> <p>Well-described documentation is more valuable than gold. After some time, you can always come back to function and remember what it did, what arguments it accepted and what it returns. Each language has its own standards, it is worth seeing and applying the description in your project.</p> <h4 id="advanced-scrapping" style="position:relative;"><a href="#advanced-scrapping" aria-label="advanced scrapping permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Advanced scrapping</h4> <p>It’s time to get data from real pages. For this, we can always use BeautifulSoup4 and Scrapy for tougher scripted sites</p> <ul> <li>How we can get information from sites?</li> <li>What is the best solution to save data as we know, that every site store them differently?</li> </ul> <h4 id="queuing-actions" style="position:relative;"><a href="#queuing-actions" aria-label="queuing actions permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Queuing actions</h4> <p>What if we have a lot of services that outsource a task to be performed? We cannot allow other connections to slow down while waiting for the action to take place. Therefore, the option is to queue tasks and process them in the background in time, when the service itself receives information that a given action has been scheduled. <a href="https://www.rabbitmq.com/" target="_blank" rel="nofollow noopener noreferrer">RabbitMQ</a> is a fairly well-designed and described queuing system that we can start with.</p> <h2 id="tools" style="position:relative;"><a href="#tools" aria-label="tools permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Tools</h2> <h4 id="docker" style="position:relative;"><a href="#docker" aria-label="docker permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>docker</h4> <p>We don’t know what we will encounter in the system, and we know that everyone on the computer has a different language version. It is worth solving with a virtual environment such as docker for both deployment and 24/7 service launch. You can immediately switch to the <a href="https://docs.docker.com/compose/" target="_blank" rel="nofollow noopener noreferrer">docker-compose</a> library - it is more convenient to use, build and configure images.</p> <h4 id="vps" style="position:relative;"><a href="#vps" aria-label="vps permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>VPS</h4> <p>It is always nice to have your own server if you want to put something to work permanently on it or for simple tests or sandbox. Google gives minimal server configuration <a href="https://cloud.google.com/free/" target="_blank" rel="nofollow noopener noreferrer">for free</a> — it works ok, I even have some services running there. It can handle docker too!</p> <h4 id="git" style="position:relative;"><a href="#git" aria-label="git permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>git</h4> <p>This tool is must-have! We have to save our projects somehow. I say right away that having <em>app, app-v2, appv3, app-v3-final, app-v3-final2</em> folders does not work (I experienced it). That is why it is worth using repositories and having control over the versions of our code. For this, <a href="https://gitlab.com" target="_blank" rel="nofollow noopener noreferrer">GitLab</a> and <a href="https://bitbucket.org" target="_blank" rel="nofollow noopener noreferrer">Bitbucket</a> give you free private repositories. Git is a necessary thing in the life of a programmer — even in the basics.</p> <h2 id="summary" style="position:relative;"><a href="#summary" aria-label="summary permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Summary</h2> <p>We must remember, that the most important thing is to try. The code we make is always good if it does what we asked for. It is important to have an initial idea of ​​how to program. After some time, you can go back and improve / rewrite the code. If we see that the old code is ugly, it means that we already have more knowledge than at the beginning. If we feel good at programming, we can move on to new things and knowledge like tests, data structures, good coding practices, communication options, queuing, etc. — There are a lot of topics, but it is worth taking when you feel confident. At the beginning, write the code so that you can do something and develop it with each new idea. Check the data, validate if the user has entered something correctly and so on. Good luck everyone, ever me!</p><![CDATA[Detailed description of your API]]>https://sawczuk.dev/posts/detailed-description-of-your-apihttps://sawczuk.dev/posts/detailed-description-of-your-apiFri, 06 Nov 2020 22:38:31 GMT<p>A good described API is attitude. Especially when planning architecture, where other teams work on its successive elements. Therefore, it is so important to describe in detail each API path. For this, it is worth using the right tool. With a growing team, you need to be aware that communication between people must be at the highest level. To do this, it is often interfered with the introduction of many management tools. With such activities, you should also remember about the list of documentation and knowledge sharing. <a href="swagger.io">Swagger</a> is one of such tool to help share information between developers about API endpoints in the project.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/fc3c2d27a44e67535b2c9719bd96391a/47311/server-5.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAMBAgQF/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAHmOvNzjHh//8QAGxAAAgEFAAAAAAAAAAAAAAAAAAECERIhMTL/2gAIAQEAAQUCSMW0FzHR/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGBAAAgMAAAAAAAAAAAAAAAAAAAEgIeH/2gAIAQEABj8CKZsP/8QAGxAAAgMAAwAAAAAAAAAAAAAAAAERITEQcZH/2gAIAQEAAT8hidwNrCxq7Oy8G5px/9oADAMBAAIAAwAAABDj3//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/EFf/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAgEBPxBH/8QAHBABAAICAwEAAAAAAAAAAAAAAQARITEQUaHw/9oACAEBAAE/EAtTKyiVMhQCgw93dMEay/W+P6I7n//Z'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/fc3c2d27a44e67535b2c9719bd96391a/8ac56/server-5.webp 240w, /static/fc3c2d27a44e67535b2c9719bd96391a/d3be9/server-5.webp 480w, /static/fc3c2d27a44e67535b2c9719bd96391a/e46b2/server-5.webp 960w, /static/fc3c2d27a44e67535b2c9719bd96391a/260c2/server-5.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/fc3c2d27a44e67535b2c9719bd96391a/09b79/server-5.jpg 240w, /static/fc3c2d27a44e67535b2c9719bd96391a/7cc5e/server-5.jpg 480w, /static/fc3c2d27a44e67535b2c9719bd96391a/6a068/server-5.jpg 960w, /static/fc3c2d27a44e67535b2c9719bd96391a/47311/server-5.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/fc3c2d27a44e67535b2c9719bd96391a/6a068/server-5.jpg" alt="Functions of Swagger" title="Functions of Swagger" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>This tool is most often found in the form of various libraries for automatic building of API documentation from the current project code. Sure, this is one of the possibilities of this project, but often people overlook the standard operation of the main tool itself. For their integration, libraries ignore many additional functionalities provided by the basic swagger tool. Also, such libraries often may not allow for some kind of adding comments or define some kind of elements as additional or existing under some specific logic. Another thing is the modification of documentation that is scattered all over the code. Therefore, it is worth taking a look at the basic operation of the Swagger Editor itself.</p> <h2 id="swagger-editor" style="position:relative;"><a href="#swagger-editor" aria-label="swagger editor permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Swagger Editor</h2> <p>The approach to Swagger can be difficult, but I encourage you to check what a well-structured endpoint description for a specific module or the entire project may look like. The example that can be found in the <a href="https://editor.swagger.io/" target="_blank" rel="nofollow noopener noreferrer">Swagger Editor</a> tool describes all the cases encountered during the API description. As I can say editor allows to do a better description of the default fields, which this option from my experience is rarely found in integrated libraries. In addition, we can also download the desktop app beside web and cloud versions.</p> <h2 id="summary" style="position:relative;"><a href="#summary" aria-label="summary permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Summary</h2> <p>Generating the final documentation and forwarding it to the next department, which can be a frontend, for example, can be a great advantage for the implementation of the project. It also solves many questions whether and what the API data returns, reduces the number of tests, speeds up the project structure and may also allow for better planning of the API before writing it. The tool and the JSON file itself or the full output should become an integral part of any larger project.</p><![CDATA[Magic of whiptail and Squid proxy installation]]>https://sawczuk.dev/posts/magic-of-whiptail-squid-proxy-instalationhttps://sawczuk.dev/posts/magic-of-whiptail-squid-proxy-instalationWed, 04 Nov 2020 21:50:47 GMT<p>When installing a bash open source project, I was always full of admiration for the developers who implemented the graphical interface. I was curious how it works and how I could apply it to my projects. The solution is simple. It is the whiptail.</p> <h2 id="whiptail" style="position:relative;"><a href="#whiptail" aria-label="whiptail permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>whiptail</h2> <p>Whiptail is one of the popular programs that allows you to display dialog boxes in the shell. Proper preparation of steps and good ability to write shell scripts allows you to skillfully extract a lot of information from it and use it to build automated scripts.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/000c3d3fbfd1f05ba679b9f2342afb08/47311/server-4.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIEAwX/xAAVAQEBAAAAAAAAAAAAAAAAAAABAP/aAAwDAQACEAMQAAAB5FcrDmIR/8QAGxABAAAHAAAAAAAAAAAAAAAAAQAEEBITMkH/2gAIAQEAAQUCtiXDJ11Kf//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABoQAAICAwAAAAAAAAAAAAAAAAABEBEhMYH/2gAIAQEABj8Czotwjkf/xAAcEAEAAQQDAAAAAAAAAAAAAAABACExQXERUbH/2gAIAQEAAT8h7I0zDBgOMxqmt54JYbRxP//aAAwDAQACAAMAAAAQuD//xAAXEQEAAwAAAAAAAAAAAAAAAAABEBEh/9oACAEDAQE/EErY/8QAFhEBAQEAAAAAAAAAAAAAAAAAAREQ/9oACAECAQE/EBrM/8QAHhABAAIBBAMAAAAAAAAAAAAAAQARQRAhMbFxocH/2gAIAQEAAT8QCFICjKCdJc6ySlUrTt5nrdHSnV9n/9k='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/000c3d3fbfd1f05ba679b9f2342afb08/8ac56/server-4.webp 240w, /static/000c3d3fbfd1f05ba679b9f2342afb08/d3be9/server-4.webp 480w, /static/000c3d3fbfd1f05ba679b9f2342afb08/e46b2/server-4.webp 960w, /static/000c3d3fbfd1f05ba679b9f2342afb08/260c2/server-4.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/000c3d3fbfd1f05ba679b9f2342afb08/09b79/server-4.jpg 240w, /static/000c3d3fbfd1f05ba679b9f2342afb08/7cc5e/server-4.jpg 480w, /static/000c3d3fbfd1f05ba679b9f2342afb08/6a068/server-4.jpg 960w, /static/000c3d3fbfd1f05ba679b9f2342afb08/47311/server-4.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/000c3d3fbfd1f05ba679b9f2342afb08/6a068/server-4.jpg" alt="Basic functions of whiptail" title="Basic functions of whiptail" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>The basic program boxes include</p> <ul> <li>message box</li> <li>yes / no box</li> <li>input box</li> <li>password box</li> <li>text box</li> <li>menu</li> <li>check list</li> <li>radio letter</li> <li>progress gauge</li> </ul> <p>So as you can see, we can ask the user for all his requirements for running the script.</p> <h2 id="squid--install-proxy-server-in-few-simple-steps" style="position:relative;"><a href="#squid--install-proxy-server-in-few-simple-steps" aria-label="squid install proxy server in few simple steps permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Squid — install proxy server in few simple steps</h2> <p>Squid is a great program for creating a proxy server. It allows you to control the flow of information, maintain access at a specific time, manage access for users or bind specific network cards to appropriate ports and more. It is a very efficient tool and its configuration at a basic level does not require much time. Therefore, recently I installed this server on one of my VPS to get to specific sources by passing my data over other IP. Of course, while it takes a few steps to install, it’s always nice to have an automated task. Therefore, I created the base code using dialog boxes. In order not to take too much space on the article, it is also slimmed down with additional validation of variables and function results. After all, the code is designed to encourage work.</p> <div class="gatsby-highlight" data-language="shellscript"><pre class="language-shellscript"><code class="language-shellscript">#!/bin/bash whiptailInputBox() { # Open window with text input # Parameters: # $1 Box title # $2 Content # $3 Default RESULT=`whiptail --inputbox &quot;$2&quot; 8 78 $3 --title &quot;$1&quot; 3&gt;&amp;1 1&gt;&amp;2 2&gt;&amp;3` return $? echo $RESULT } whiptailPasswordBox() { # Open window with password input # Parameters: # $1 Box title # $2 Content # $3 Default echo `whiptail --passwordbox &quot;$2&quot; 8 78 $3 --title &quot;$1&quot; 3&gt;&amp;1 1&gt;&amp;2 2&gt;&amp;3` return $? echo $RESULT } whiptailMessageBox() { # Open window as message box # Parameters: # $1 Box title # $2 Content whiptail --title &quot;$1&quot; --msgbox &quot;$2&quot; 8 78 } preinstall() { if ! [ -x &quot;$(command -v whiptail)&quot; ]; then /usr/bin/apt update /usr/bin/apt -y install whiptail fi } install_squid() { /usr/bin/apt update /usr/bin/apt -y install apache2-utils squid3 } squid_configuration() { # Revove old squid.conf config /bin/rm -f /etc/squid/squid.conf /usr/bin/touch /etc/squid/blacklist.acl # Update iptables and accept port /sbin/iptables -I INPUT -p tcp --dport 3128 -j ACCEPT /sbin/iptables-save # Reset service service squid restart update-rc.d squid defaults } preinstall whiptailMessageBox &quot;Info&quot; &quot;Auto installation script will guide you with configuration of squid package&quot; SQUID_USERNAME=$(whiptailInputBox &quot;Squid Username&quot; &quot;Provide SQUID username&quot; &quot;&quot;) if [ $? != 0 ]; then exit; fi SQUID_PASSWORD=$(whiptailPasswordBox &quot;Squid Password&quot; &quot;Provide SQUID password&quot; &quot;&quot;) if [ $? != 0 ]; then exit; fi install_squid /usr/bin/htpasswd -b -c /etc/squid/passwd $SQUID_USERNAME $SQUID_PASSWORD squid_configuration whiptailMessageBox &quot;Info&quot; &quot;Instalation complete&quot;</code></pre></div> <p>With squid already installed, we can go to configuration. The current <code class="language-text">squid.conf</code> allows us only to use the proxy locally on the server. To unlock its external use, we must remember that each service published by us should be secured. There are too many IP lists of proxy servers on the Internet that have been exposed to the public with different port values in hope that no one will guess why they are open.</p> <p>To protect yourself against such situations, it is enough to unblock the proxy entry only for credentials that we created earlier using the script above. The record should be placed under the indicated comment in the <code class="language-text">/etc/squid/squid.conf</code> file. After that, all you need it reset the service and you can connect to your proxy service.</p> <div class="gatsby-highlight" data-language="dotenv"><pre class="language-dotenv"><code class="language-dotenv"># # INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS # auth_param basic program /usr/lib/squid/basic_ncsa_auth /etc/squid/passwd auth_param basic children 5 auth_param basic realm Squid Basic Authentication auth_param basic credentialsttl 2 hours acl auth_users proxy_auth REQUIRED http_access allow auth_users</code></pre></div> <h2 id="tips" style="position:relative;"><a href="#tips" aria-label="tips permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Tips</h2> <p>curl command can be used to test a connection through a proxy</p> <div class="gatsby-highlight" data-language="bashscript"><pre class="language-bashscript"><code class="language-bashscript">curl -x http://user.password@domain_or_IP:3128 -I http://google.com # When success (Message head) HTTP/1.1 301 Moved Permanently Location: http://www.google.com/ # When conection is secured with proxy (Message head) HTTP/1.1 407 Proxy Authentication Required</code></pre></div> <p>Likewise, we can get our IP after going through any proxy we provide</p> <div class="gatsby-highlight" data-language="bashscript"><pre class="language-bashscript"><code class="language-bashscript">wget -e use_proxy=yes -e http_proxy=http://user.password@domain_or_IP:3128 -qO- http://ipecho.net/plain | xargs echo</code></pre></div><![CDATA[Your own VPN service in USA]]>https://sawczuk.dev/posts/your-own-vpn-service-in-usahttps://sawczuk.dev/posts/your-own-vpn-service-in-usaSun, 01 Nov 2020 21:37:05 GMT<p>Regional locks aren’t nice. They always cause a problem with access to certain sources. VPN service charges are often high, and the free versions only offer a certain usage limit or are bandwidth-constrained. Therefore, why not set up your own free VPN server overseas?</p> <h2 id="virtal-private-server" style="position:relative;"><a href="#virtal-private-server" aria-label="virtal private server permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Virtal Private Server</h2> <p>There are a lot of VPS offers on the Internet. This time the search is directed a bit differently than the access and HTTP servers. The first thing we need to pay attention to is where the service is run, and then what is the suggested rental price of the resources. From large suppliers, we may be interested in well-known service providers such as OVH, AWS, Azure or Google. They can often have strong server rental deals or special free-tier offers. However, be careful whether the server will not be refunded after the end of the promotional period.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/0c1a52aeacc5dcaf5e0da19a48c4350d/47311/server-3.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMFBP/EABcBAAMBAAAAAAAAAAAAAAAAAAECAwT/2gAMAwEAAhADEAAAAZw3VXPOGAb/xAAZEAACAwEAAAAAAAAAAAAAAAAAAQIDEUH/2gAIAQEAAQUClEVePcEcP//EABcRAQADAAAAAAAAAAAAAAAAAAABERL/2gAIAQMBAT8B1K3/xAAXEQEAAwAAAAAAAAAAAAAAAAAAAQIR/9oACAECAQE/AYqx/8QAGBAAAwEBAAAAAAAAAAAAAAAAABCBETH/2gAIAQEABj8C1cKRf//EABwQAAIBBQEAAAAAAAAAAAAAAAABESExQWFxUf/aAAgBAQABPyGaiOoqitowNFpmYYe9P//aAAwDAQACAAMAAAAQf+//xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAwEBPxCA/8QAFhEBAQEAAAAAAAAAAAAAAAAAAQAh/9oACAECAQE/EATZn//EAB4QAQACAgIDAQAAAAAAAAAAAAEAESExcbFBYYHw/9oACAEBAAE/EK6KF2Xn1cVAVvhlrdTK1VwDZN+Lufj8hvk7n//Z'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/0c1a52aeacc5dcaf5e0da19a48c4350d/8ac56/server-3.webp 240w, /static/0c1a52aeacc5dcaf5e0da19a48c4350d/d3be9/server-3.webp 480w, /static/0c1a52aeacc5dcaf5e0da19a48c4350d/e46b2/server-3.webp 960w, /static/0c1a52aeacc5dcaf5e0da19a48c4350d/260c2/server-3.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/0c1a52aeacc5dcaf5e0da19a48c4350d/09b79/server-3.jpg 240w, /static/0c1a52aeacc5dcaf5e0da19a48c4350d/7cc5e/server-3.jpg 480w, /static/0c1a52aeacc5dcaf5e0da19a48c4350d/6a068/server-3.jpg 960w, /static/0c1a52aeacc5dcaf5e0da19a48c4350d/47311/server-3.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/0c1a52aeacc5dcaf5e0da19a48c4350d/6a068/server-3.jpg" alt="Solutions for own media center" title="Solutions for own media center" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>In paid offers from service providers you can find very cheap servers listed below</p> <ul> <li>Vultr: Worldwide locations, IPv6 support, starting at $3.50/month</li> <li>Hetzner: Germany, IPv6, 20 TB of traffic, starting at €3/month</li> <li>Digital Ocean: Worldwide locations, IPv6 support, starting at $5/month</li> <li>PulseHeberg: France, unlimited bandwidth, starting at €3/month</li> <li>OVH: Worldwide locations, IPv6 support, starting at $3.50/month</li> </ul> <p>Currently, the most attractive offer, in my opinion, can boast of google cloud, which offers a <a href="https://cloud.google.com/free" target="_blank" rel="nofollow noopener noreferrer">full free-tier</a> for a machine located in Oregon, Iowa or South Carolina.</p> <h2 id="free-tier-in-google-cloud-platform" style="position:relative;"><a href="#free-tier-in-google-cloud-platform" aria-label="free tier in google cloud platform permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Free-tier in Google Cloud Platform</h2> <p>Basic server configuration starts with creating a <a href="https://console.cloud.google.com/compute" target="_blank" rel="nofollow noopener noreferrer">Compute Engine</a> instance. When creating it, we must remember about the appropriate region — although here the choice is more about continents. The instance that interests us is F1-micro, which will be enough for us to provide this and several other services. The next step will be to connect to the server via SSH and install OpenVPN. Unfortunately, for first-time users, installing OpenVPN can be a bit complicated. Large descriptions of functions in the documentation, no direct support for some distributions, configuration of files without full understanding. Unfortunately, this is not the simplest solution. Therefore, for users who do not want to play with the configuration of the server to support the entire company, but only for private purposes, very nice repositories have been created to auto-install this service.</p> <ul> <li><a href="https://github.com/Nyr/openvpn-install" target="_blank" rel="nofollow noopener noreferrer">Simplest instalation</a></li> <li><a href="https://github.com/angristan/openvpn-install" target="_blank" rel="nofollow noopener noreferrer">If someone want more controll over configs</a></li> </ul> <p>After installation, all you need to remember is editing firewall rules to allow incoming traffic from another port. For GCP, we can find it at <a href="https://backports.debian.org/Instructions/" target="_blank" rel="nofollow noopener noreferrer">this link</a>. It is suggested to add a new rule, assign the VPN ports, add a tag to the Compute Engine instance and reset the system to accept all changes.</p> <h2 id="summary" style="position:relative;"><a href="#summary" aria-label="summary permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Summary</h2> <p>If someone can install servers quickly or has their VPS abroad, this is a perfect opportunity to have a VPN for streaming services. Thanks to this, we will be able to use the advantages of service providers such as NordVPN or ExpressVPN without the need to buy a subscription or free providers with trial access and shady data usage. In addition, the data that passes through the server is ours, so there is less risk of it being viewed by an unknown person from an unknown proxy server.</p><![CDATA[Task runner that resolves missing pipeline events]]>https://sawczuk.dev/posts/task-runner-that-resolves-missing-pipeline-eventshttps://sawczuk.dev/posts/task-runner-that-resolves-missing-pipeline-eventsSun, 01 Nov 2020 17:01:53 GMT<p>When creating pipelines for releasing things to the environment, we may run into the problem of handling too few events. While searching the internet, I encountered an interesting task runner library. Upon closer acquaintance with the library, it will be possible to supplement the missing functions of pipelines offered in cloud solutions.</p> <h2 id="makefile-bash-or-something-different" style="position:relative;"><a href="#makefile-bash-or-something-different" aria-label="makefile bash or something different permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Makefile, bash or something different?</h2> <p>Bash or Makefile files are most often used when building commands to improve the output. While this solution runs all scripts and allows for a large management of functions, it lacks error handling or API communication. <a href="https://pypyr.io/" target="_blank" rel="nofollow noopener noreferrer">Pypyr</a> is a task runner for automation pipelines script as sequential task workflow steps in yaml. </p> <p>It has many functions, thanks to which we can properly automate the work of tasks. Most importantly, it has been quite successful at capturing and working on exceptions. Steps of activities can be combined, tasks described in detail, and also modified using environmental variables and data stored in the middle of the task. Additionally, as a basic functionality, we can also write logical conditions and even run python code. This solution gives a lot of possibilities for code actions. It is certainly easier to manage in already typical Python projects, where the work of publishing tasks does not have to be done by bash itself. While the first meeting on the website with the library may scare you off with its structure at the beginning, further delving into the topic shows that complex tasks can be much clearer for future activities in the project.</p> <div class="gatsby-highlight" data-language="yaml"><pre class="language-yaml"><code class="language-yaml"><span class="token comment"># This is an example showing the anatomy of a pypyr pipeline</span> <span class="token comment"># A pipeline should be saved as {working dir}/mypipelinename.yaml.</span> <span class="token comment"># Run the pipeline from {working dir} like this: pypyr mypipelinename</span> <span class="token comment"># optional. set this to pass cli arguments to the pipeline.</span> <span class="token key atrule">context_parser</span><span class="token punctuation">:</span> my.custom.parser <span class="token comment"># mandatory.</span> <span class="token key atrule">steps</span><span class="token punctuation">:</span> <span class="token comment"># step-group. every pipeline starts with steps unless you tell pypyr differently.</span> <span class="token punctuation">-</span> my.package.my.module <span class="token comment"># simple step pointing at a python module in a package</span> <span class="token punctuation">-</span> mymodule <span class="token comment"># simple step pointing at a python file</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> pypyr.steps.default <span class="token key atrule">in</span><span class="token punctuation">:</span> <span class="token key atrule">defaults</span><span class="token punctuation">:</span> <span class="token key atrule">ifConinue</span><span class="token punctuation">:</span> <span class="token boolean important">False</span> <span class="token comment"># optional. Check if command have set on ifContinue variable, if not, set default.</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> my.package.another.module <span class="token comment"># complex step. It contains a description and in parameters.</span> <span class="token key atrule">description</span><span class="token punctuation">:</span> Optional description is for humans. It's any text that makes your life easier. <span class="token key atrule">in</span><span class="token punctuation">:</span> <span class="token comment"># optional. Set these key-value pairs in context for this step.</span> <span class="token key atrule">parameter1</span><span class="token punctuation">:</span> value1 <span class="token key atrule">parameter2</span><span class="token punctuation">:</span> value2 <span class="token key atrule">run</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>ifContinue<span class="token punctuation">}</span> <span class="token comment"># optional. Runs this step if True, skips step if False. Defaults True if not specified.</span> <span class="token key atrule">skip</span><span class="token punctuation">:</span> <span class="token boolean important">False</span> <span class="token comment"># optional. Skips this step if True, runs step if False. Defaults False if not specified.</span> <span class="token key atrule">swallow</span><span class="token punctuation">:</span> <span class="token boolean important">False</span> <span class="token comment"># optional. Swallows any errors raised by the step. Defaults False if not specified.</span> <span class="token comment"># optional.</span> <span class="token key atrule">on_success</span><span class="token punctuation">:</span> <span class="token comment"># step-group</span> <span class="token punctuation">-</span> my.first.success.step <span class="token punctuation">-</span> my.second.success.step <span class="token comment"># optional.</span> <span class="token key atrule">on_failure</span><span class="token punctuation">:</span> <span class="token comment"># step-group</span> <span class="token punctuation">-</span> my.failure.handler.step <span class="token punctuation">-</span> my.failure.handler.notifier</code></pre></div> <h2 id="summary" style="position:relative;"><a href="#summary" aria-label="summary permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Summary</h2> <p> The options provided by the pipeline configured in this way allow you to control the issue and with the appropriate status of a task or individual steps, we can even send an appropriate notification or withdraw changes from the server. <a href="https://pypyr.io/" target="_blank" rel="nofollow noopener noreferrer">Pypyr</a> is a nice tool that can come in handy for more complex project steps and can become a good alternative to the Makefile’s all-in-one tasks.</p><![CDATA[Media library with 2TB cloud storage on VPS]]>https://sawczuk.dev/posts/vps-with-2tb-cloud-storagehttps://sawczuk.dev/posts/vps-with-2tb-cloud-storageSat, 31 Oct 2020 13:50:26 GMT<p>I have to admit, having your own private Netflix-like service is really nice.</p> <p>The project I have had contact with recently is Jellyfin. It is a very welcoming and multi-device media software system. The project, as in the case of kodi, allows you to manage the media library, download and complete information about the downloaded content, it has an automatic season organizer for TV Shows and allows you to watch Live TV &#x26; DVR and more. All this is available under the www panel placed on our home server or VPS. For all devices and access from everywhere.</p> <h2 id="data-storage-problem" style="position:relative;"><a href="#data-storage-problem" aria-label="data storage problem permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Data storage problem</h2> <p>Ever if you install media center on the server and want to store a collection of movies to watch, you usually don’t have much disk space. It is understandable, because we do not attach large NAS drives or other heavy devices to each of our servers.</p> <p>Unfortunately, for VPS servers, the matter of renting additional space becomes even more complicated Therefore, before approaching the topic, you should consider what problems you may encounter when accessing and reading/writing multimedia from the server.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/e675a6d678f5769c36648cfdfe63fd90/47311/server-1.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAQFAgP/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAv/aAAwDAQACEAMQAAABjvzO6ciwr//EABoQAAICAwAAAAAAAAAAAAAAAAECADIDEBL/2gAIAQEAAQUCxXdU5BQQW1//xAAVEQEBAAAAAAAAAAAAAAAAAAAQQf/aAAgBAwEBPwGn/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAHBAAAQMFAAAAAAAAAAAAAAAAAAECERAiQVGR/9oACAEBAAY/AkkudOoMiHKf/8QAGxABAAICAwAAAAAAAAAAAAAAAQAREEFRgZH/2gAIAQEAAT8hpUBGEuXFAFPjG31h/9oADAMBAAIAAwAAABBoz//EABURAQEAAAAAAAAAAAAAAAAAABAx/9oACAEDAQE/EIf/xAAWEQEBAQAAAAAAAAAAAAAAAAABEBH/2gAIAQIBAT8QTSf/xAAdEAACAgEFAAAAAAAAAAAAAAAAAREhYTGhscHw/9oACAEBAAE/EFiwbklh2rTSxSdkKJYT2jfrgWv1Qz//2Q=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/e675a6d678f5769c36648cfdfe63fd90/8ac56/server-1.webp 240w, /static/e675a6d678f5769c36648cfdfe63fd90/d3be9/server-1.webp 480w, /static/e675a6d678f5769c36648cfdfe63fd90/e46b2/server-1.webp 960w, /static/e675a6d678f5769c36648cfdfe63fd90/260c2/server-1.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/e675a6d678f5769c36648cfdfe63fd90/09b79/server-1.jpg 240w, /static/e675a6d678f5769c36648cfdfe63fd90/7cc5e/server-1.jpg 480w, /static/e675a6d678f5769c36648cfdfe63fd90/6a068/server-1.jpg 960w, /static/e675a6d678f5769c36648cfdfe63fd90/47311/server-1.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/e675a6d678f5769c36648cfdfe63fd90/6a068/server-1.jpg" alt="Solutions for own media center" title="Solutions for own media center" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <h4 id="home-server" style="position:relative;"><a href="#home-server" aria-label="home server permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Home server</h4> <p>The first solution is to have your own home server. However, this requires an investment in an additional large external drive, and the design itself does not look so nice. Of course, for most people, the very result of the work will be important. Such a server will have a problem with exposing the service to the world, but you can connect to it using DDNS or VPN service — which unfortunately makes the availability of the device difficult if we are away from home. Another disadvantage is the amount of session maintenance and usage of CPU for video encoding. It may turn out that it will not be possible to support more than two users at the same time. If these drawbacks are not that important to someone, you can always use this solution.</p> <h4 id="vps" style="position:relative;"><a href="#vps" aria-label="vps permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>VPS</h4> <p>Purchasing a private server from the service provider will allow us to obtain more resources to use. It is also a good alternative, because we do not have to worry about access, and we know that the server itself is protected against failures. The only problem is usable space, which often ends with the offered 20 GB SSD.</p> <h4 id="cloud-storage" style="position:relative;"><a href="#cloud-storage" aria-label="cloud storage permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Cloud Storage</h4> <p>There are a couple of options to get around this limit. The first one is to buy storage from the server provider itself. Unfortunately, this is probably the most expensive option. The second option is to purchase services for blop files. It works on the basis of file transfer in the form of file fragmentation of 20 MB. One such service is <a href="https://wasabi.com/" target="_blank" rel="nofollow noopener noreferrer">Wasabi</a>, a good alternative to enlarge the server disk cheaper than Amazon S3, with no API or egress fees. If someone would like to test this service, there should still be a option for a first month free. The third option is to use a regular private cloud for data synchronization. This is quite an interesting option, but you have to read which clouds will be useful for use on the server. Private clouds have two modes, the first is to copy physical files 1:1, the second is to send files to the cloud and access them on demand, and we treat the file structure as links. For low capacity servers use the second option. After checking out the cloud offerings, I found that Dropbox was the easiest one (there are other service providers, of course). If you have an account with likely 2 TB of cloud data, it is good to connect it to one of the folders on the server. For known cloud services, it is strongly suggested using the <a href="https://rclone.org/" target="_blank" rel="nofollow noopener noreferrer">rclone</a> program for simple authorization on linux systems. In addition, it has the option of data encryption and decryption, so only people with the key will be able to see the real content of the cloud. After such an operation, our server with large capacity for data storage will be ready.</p> <h2 id="summary" style="position:relative;"><a href="#summary" aria-label="summary permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Summary</h2> <p>The last step is to install <a href="https://jellyfin.org/" target="_blank" rel="nofollow noopener noreferrer">Jellyfin</a> mentioned at the very beginning of post and select the folder of stored movie libraries. I also suggest installing an FTP server for easier file transfer to the server. Consequently, if someone is more advanced in console server operation, <a href="https://linux.die.net/man/1/rsync" target="_blank" rel="nofollow noopener noreferrer">rsync</a> is a good alternative. With such activities, what is left is to create an account, send files and enjoy the new private media service. I have been using the current solution for six months now and I must admit that I do not feel the need to switch to another platform. It is also especially good if several people work on one such website. Then it’s easier to find something new to watch and share your insights.</p><![CDATA[Reboot your old Raspberry Pi for home DNS + VPN]]>https://sawczuk.dev/posts/reboot-your-raspberry-pihttps://sawczuk.dev/posts/reboot-your-raspberry-piTue, 27 Oct 2020 23:08:49 GMT<p>Every time I want to get on with the Raspberry Pi topic, it goes back to the closet after a few days. The problem with running on a home server is so bothersome that we have to do everything ourselves, from installation to configuration. So while the very idea of using the device is wonderful, its implementation and spent time can surpass us. Therefore, it is worth realizing that the most changes are introduced by the method of small steps. By reusing the Raspberry Pi lying in the corner, the first step might be to run a simple ad-filtering DNS server on it. The next step could be configuring a VPN service to get from anywhere to home network devices.</p> <h2 id="current-server-situation" style="position:relative;"><a href="#current-server-situation" aria-label="current server situation permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Current server situation</h2> <p>As you know, I deal with photography. The problem that this creates is storing photos for processing. I have had many attempts to store photos on physical, portable and cloud drives. Unfortunately, none of these solutions proved to be 100% successful. The reasons were different. From problems with synchronization, disk filling, downloading blop from the clouds, disk loss or keeping photos only on one device. Therefore, the current solution I have is to use the NAS server with 2 TB storage itself. Of course, I’m not saying that this is the perfect solution, but it meets all my requirements at the moment. Almost. This fact forced me to take action and install two really nice server projects for Debian devices. Thanks to the operation, from today I can use and view my photo collections from anywhere. And in fact, I’m happy with the current result.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; " > <a class="gatsby-resp-image-link" href="/static/0406950420859870f33ed83b1e3fe583/47311/server-6.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBP/EABYBAQEBAAAAAAAAAAAAAAAAAAABAv/aAAwDAQACEAMQAAABytPCSxf/xAAaEAACAgMAAAAAAAAAAAAAAAAAARFBEiEy/9oACAEBAAEFAp1eDGWuP//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/AUf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAdEAABAgcAAAAAAAAAAAAAAAAAASECEBExQXGB/9oACAEBAAY/AncvQzySEOj/xAAfEAACAQIHAAAAAAAAAAAAAAABEQAhURAxQXGBodH/2gAIAQEAAT8heodp9lEJdYy6j0jKOYO44H//2gAMAwEAAgADAAAAEJvf/8QAFhEAAwAAAAAAAAAAAAAAAAAAEBFB/9oACAEDAQE/EKg//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHxABAQABAwUBAAAAAAAAAAAAAREAITFREEGBocGx/9oACAEBAAE/EFjEsAicQa3EU5QNk5j937YBWhWCh5DXPa+OhvTfrn//2Q=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/0406950420859870f33ed83b1e3fe583/8ac56/server-6.webp 240w, /static/0406950420859870f33ed83b1e3fe583/d3be9/server-6.webp 480w, /static/0406950420859870f33ed83b1e3fe583/e46b2/server-6.webp 960w, /static/0406950420859870f33ed83b1e3fe583/260c2/server-6.webp 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/webp" /> <source srcset="/static/0406950420859870f33ed83b1e3fe583/09b79/server-6.jpg 240w, /static/0406950420859870f33ed83b1e3fe583/7cc5e/server-6.jpg 480w, /static/0406950420859870f33ed83b1e3fe583/6a068/server-6.jpg 960w, /static/0406950420859870f33ed83b1e3fe583/47311/server-6.jpg 1080w" sizes="(max-width: 960px) 100vw, 960px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/0406950420859870f33ed83b1e3fe583/6a068/server-6.jpg" alt="Preparation for RPi server use" title="Preparation for RPi server use" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <h2 id="pihole" style="position:relative;"><a href="#pihole" aria-label="pihole permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>PiHole</h2> <p>I omit the information that I am starting the topic in reverse order as I was installing services on the Raspberry Pi. I am simply aware that this topic will be more interesting and will be useful to a wider audience. There is one reason. This is the fastest way to get rid of bothersome ads while surfing the internet. Most of us probably use the AdBlock browser plugin. Unfortunately, the last year shown that there is a way to found if plugin is enabled, and some sites catch it’s and show us pop-up messages. The second problem is installing it on every device, bypassing the phone — unfortunately we have to use a clean browser instance here… Therefore, a great choice is to use the DNS service, which will block ads from showing up from the very network level. The advantages for sure will be:</p> <ul> <li>no ads</li> <li>no popups asking for whitelisting the page</li> <li>faster page loading, because we do not load additional ad resources</li> <li>statistics</li> </ul> <p>The project that can be used is widely discussed <a href="https://pi-hole.net/" target="_blank" rel="nofollow noopener noreferrer">PiHole</a>. Its simple installation allows to quickly load settings with ready-to-go configuration. The last step is only to enter the router itself and, if possible, set static IP for our Raspberry and replace the primary DNS IP with the address of our server in DHCP options. After that, we could probably see our PiHole panel under <a href="http://pi.hole/" target="_blank" rel="nofollow noopener noreferrer">pi.hole</a> domain address. In a few hours, it could show that likely 30% of network traffic on websites are ads. And we are free of them.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; " > <a class="gatsby-resp-image-link" href="/static/21f38bbb31c8f0e6c96300e116e12f69/5a190/pi-hole.png" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsSAAALEgHS3X78AAACD0lEQVQozz2Sy27TUBRFM2FSJD4AJJK0cfy27/V1/KzTJG1KKREpLU8BAgk64CkkBhQxYIYYMQMJBjBH4h/4s8VxKjo42j4+V/tur+NOlDc4UYKVpXQzzUaR0he1spJePaff7K5qo5qxWURs1x6zOmBa+xRGMVYJO3nJNDFs6YSOqSrcMGH4eA/35IhzDzM6hy6X7t8h/vKbtY8/6Lz7ytqT93x7m/P3k8OfNxf49ew8H27E/Dxa8n2W8jnsc6JsOpYb0LNcgq2GcHfCcFKxUadY5Sbu/ID+bEFveo31eoftieH6LObqls/e2GOcJ8zlS+bGMFPqNGGgR9iBItI5XqDxJW1brh/jyGW+HeB5AXYcYw1iusmI9cDQj1IGvmY9VCu1PM0wMnRc4Tf0FaHJccTQludWXU8xTFMuLje5vBwzECTdJiVdCk/prSbDlzO9/ZruvGBQpjjC8SyhbzLsMsMpxbgucEX9+Rj96ib5i9tcef2I+Pkh9fEtqpd3SRe7uEmKubdAHx8QPV3i7U9PDduU8ahCCTddNUR5hZdmBJI6UQVK5ag4Q+uSSI1WfaAzPAkRyRmdFMRSoSnotOlWDKXxxLjdeFuOvHNC4dLORW0xGPohrqmEp8ERY+f/rFVh3gZbGZ4xlGFb7aBVTxYVNHuEkwXq2gNcMQm3D+SP2MfLx7JE4b8zZSA825krF/0DCBcnqahsB1wAAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/21f38bbb31c8f0e6c96300e116e12f69/8ac56/pi-hole.webp 240w, /static/21f38bbb31c8f0e6c96300e116e12f69/d3be9/pi-hole.webp 480w, /static/21f38bbb31c8f0e6c96300e116e12f69/d00b9/pi-hole.webp 800w" sizes="(max-width: 800px) 100vw, 800px" type="image/webp" /> <source srcset="/static/21f38bbb31c8f0e6c96300e116e12f69/8ff5a/pi-hole.png 240w, /static/21f38bbb31c8f0e6c96300e116e12f69/e85cb/pi-hole.png 480w, /static/21f38bbb31c8f0e6c96300e116e12f69/5a190/pi-hole.png 800w" sizes="(max-width: 800px) 100vw, 800px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/21f38bbb31c8f0e6c96300e116e12f69/5a190/pi-hole.png" alt="PiHole admin dashboard" title="PiHole admin dashboard" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <h2 id="pivpn" style="position:relative;"><a href="#pivpn" aria-label="pivpn permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>PiVPN</h2> <p>PiVPN is an interesting project that I came across recently by accident. It is based on the philosophy of simple, automatic installation, just like PiHole. During configuration, we have the option of choosing two sites for installation. These will be OpenVPN services probably known by the majority and WireGuard. During the selection, I suggested information about easier WireGuard configuration on mobile devices. And in fact, as it turns out, configuration and performance as well as lower battery consumption are really an advantage of WireGuard. After a long analysis of these two solutions, it turns out that the program itself is very optimal (the entire code takes only 4000 lines, so you can read it at lunch and make sure it is safe). Additionally, there is an option to generate connection keys and a QR code for mobile devices. So it turns out to be a good competition to OpenVPN. After installation, it is worth remembering about redirecting ports to the server and, if we do not have a static public IP address, use DDNS services.</p> <h2 id="and-so" style="position:relative;"><a href="#and-so" aria-label="and so permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>And so…</h2> <p>Without much describing how to install the projects, because the entire manual is on their pages, I can say that the whole process took me two hours. Of course, this includes finding a card reader and writing a clean image for the Raspberry Pi. So, if in the evening someone does not know what they could do, and has some time, I encourage trying to install these two services and improve own home network.</p> <p>Any action to the future can improve access to information needed in time. Therefore, it is worth taking advantage of this opportunity and installing these services ever for the future tasks.</p>