<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: dennislwm</title>
    <description>The latest articles on DEV Community by dennislwm (@dennislwm).</description>
    <link>https://dev.to/dennislwm</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F224993%2Fe24ca68f-227d-45f8-9e53-243a4c7faeaa.jpeg</url>
      <title>DEV Community: dennislwm</title>
      <link>https://dev.to/dennislwm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dennislwm"/>
    <language>en</language>
    <item>
      <title>Mastering Terraform testing, a layered approach to testing complex infrastructure</title>
      <dc:creator>dennislwm</dc:creator>
      <pubDate>Fri, 05 Apr 2024 04:10:46 +0000</pubDate>
      <link>https://dev.to/dennislwm/mastering-terraform-testing-a-layered-approach-to-testing-complex-infrastructure-4b7p</link>
      <guid>https://dev.to/dennislwm/mastering-terraform-testing-a-layered-approach-to-testing-complex-infrastructure-4b7p</guid>
      <description>&lt;p&gt;&lt;a href="https://mattias.engineer/posts/hashitalks-2024/"&gt;HashiTalks 2024: Mastering Terraform Testing, a layered approach to testing complex infrastructure&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;HashiCorp Ambassador Mattias Fjellstrom talks about a five-tiered strategy for mastering Terraform testing, in an online community event HashiTalks 2024.&lt;/p&gt;

&lt;p&gt;The first layer consists of both imperative and declarative validations. Imperative validation is where you run CLI commands such as &lt;code&gt;terraform init&lt;/code&gt;, &lt;code&gt;terraform validate&lt;/code&gt;, and &lt;code&gt;terraform plan&lt;/code&gt;. You validate not only your HCL configuration of your resources, but also your providers and desired changes. Declarative validation leverages &lt;code&gt;validation&lt;/code&gt; blocks within various Terraform components, like variables, outputs, resources, etc.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;variable "location" {
  type = string

  validation {
    condition     = contains([
      "swedencentral",
      "westeurope",
      "northeurope"
    ], var.location)
    error_message = "Use an approved location!"
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With validation in place, Layer 2 implements the Terraform test framework, starting from Terraform v1.6.0, emphasizing the importance of testing the entire module interface.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When you write tests for your Terraform modules you want to make sure to test the full module interface. What is part of the module interface? Generally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Variables.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Outputs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Any resource that is externally visible and of importance to your module consumers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Any behaviour or functionality that is exposed by the resources or data sources that are part of your module.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;By default, tests within Terraform create real infrastructure, where you can override the normal testing behaviour by updating the &lt;code&gt;command&lt;/code&gt; attribute within a &lt;code&gt;run&lt;/code&gt; block, i.e. &lt;code&gt;command = plan&lt;/code&gt; instructs Terraform to not create new infrastructure, which allows test authors to validate logical operations and custom conditions within their infrastructure in a process analogous to unit testing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Most of your tests should use &lt;code&gt;command = plan&lt;/code&gt; since these tests will be fast. When it comes to testing your validation logic many (all?) of the tests will be using the &lt;code&gt;expect_failure&lt;/code&gt; array instead of &lt;code&gt;assert&lt;/code&gt; blocks.&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;run "should_not_allow_invalid_location"{
    command = plan
    variables {
        location = "westus"
    }
    expect_failures = [
        var.location
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;p&gt;Layer 3 adds integration testing, where you have a few options on how to handle dependencies. The options range from testing dependencies by creating real infrastructure, to using a helper module, to using static values or mocks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Turn your compatibility requirements into tests, for instance if your last three major versions of a module is expected to be supported together with a third-party module, make sure that this is the case by using integration testing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Layer 4 involves testing end-to-end scenarios with complex infrastructure and operations. The author demonstrates how helper modules are required to perform things such as setting up dependencies and query for information.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Take the testing to the extreme by identifying use-cases or scenarios that your modules are intended to support.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Layer 5 shifts left by identifying patterns in your validation code and creating policies from them. The benefit of policies is that it can be easier to administer and offers different enforcement levels. The author picks HashiCorp Sentinel policies as it is integrated with Terraform Cloud.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Introduce policy-as-code in your organization. You might do this from day 1 or you might do it whenever your organization is ready for it. Start small with a set of common policies for the whole organization, then expand the scope and the number of policies successively.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>terraform</category>
      <category>hashicorp</category>
      <category>tdd</category>
      <category>devops</category>
    </item>
    <item>
      <title>What denotes a technology company?</title>
      <dc:creator>dennislwm</dc:creator>
      <pubDate>Fri, 05 Apr 2024 03:59:44 +0000</pubDate>
      <link>https://dev.to/dennislwm/what-denotes-a-technology-company-iog</link>
      <guid>https://dev.to/dennislwm/what-denotes-a-technology-company-iog</guid>
      <description>&lt;p&gt;&lt;a href="https://lethain.com/ex-technology-companies"&gt;Ex-technology companies&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Will Larson, a writer and a software engineering leader, delves into a thought-provoking inquiry posed by software engineers: "What denotes a technology company?" His exploration yields several insights into the essence of what truly defines a company as a technology entity.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The first perspective, aka. the investor perspective, is Ben Thompson's "Software has zero marginal costs". You're a technology company if adding your next user doesn't create more costs to support that user. Yes, it's not &lt;em&gt;really&lt;/em&gt; zero, e.g. &lt;strong&gt;Stripe&lt;/strong&gt; has some additional human overhead for managing fraud for each incremental user it adds, but it's a sufficiently low coefficient that it's effectively zero.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, the above perspective may be under fitting, as it overlooks some companies, such as Visa, which exhibit negligible marginal costs upon adding new users. Moreover, merely adopting a technology doesn't elevate a company to the status of a tech company, which is why entities that utilizes current software are no more than users of the most modern SaaS applications.&lt;/p&gt;

&lt;p&gt;This distinction is evident when considering companies such as &lt;strong&gt;WeWork&lt;/strong&gt; and &lt;strong&gt;Peloton&lt;/strong&gt; , which have more in common with companies that own physical assets than a tech company, where the former company leases empty buildings and converts them into office space, and the latter sells home fitness equipment and streaming classes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The second perspective, aka. the employee perspective, on being a technology company is captured by Camille Fournier, "A company where engineering has a seat at the table for strategic discussions." If engineering has a meaningful influence in how the company makes decisions, then doing engineering work at that company will generally be a rewarding experience. If they don't have much influence, then they'll generally be treated as a cost center.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By applying both perspectives in the case of WeWork and Peloton, we may be able to classify Peloton as a tech company, while WeWork falls short of the definition.&lt;/p&gt;

&lt;p&gt;From the investor perspective, adding a user to Peloton comes with the costs of both hardware (e.g. bicycle and cloud infrastructure) and software (e.g. streaming software). The marginal cost benefits will come mostly from the software component, as Peloton adds more users. In contrast, adding a user to WeWork comes with primarily the cost of hardware (e.g. office space, utilities), but adopting a software to manage its users base does not make WeWork a tech company.&lt;/p&gt;

&lt;p&gt;From the employee perspective, WeWork's investment in its software would more likely be treated as a cost centre, rather than an asset. On the other hand, Peloton's investment in its streaming software is likely classified as an asset that will generate revenue from the next user.&lt;/p&gt;

&lt;p&gt;However, Peloton may not be classified as a fully tech company, in the sense that it can never reach the ideal zero marginal costs, due to its hardware cost that increases for each additional user, unlike companies such as &lt;strong&gt;Netflix&lt;/strong&gt; , &lt;strong&gt;Facebook&lt;/strong&gt; , etc that may approach zero marginal costs in the long run.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The third perspective, aka the innovator's perspective, centers on Clayton Christensen's "The Innovator's Dilemma", suggesting that companies who are creating or leveraging disruptive technologies are tech companies. In this case, only businesses that obsolete a previous generation of businesses deserve the moniker, and only history can ordain whether a company is a technology company. In a future where Tesla successfully disrupts the existing automotive industry giants, they are such a company; in a future where they fail to build a durable business, they are not. This definition, reaching back from the future to judge todays companies, is interesting but not very useful.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The author labels Peloton a "tech company" due to its disruptive technology, in this case creating a new market for online cycling classes and taking away market share from physical gym classes. But it is yet to be seen if Peloton can build a durable business that will make obsolete the physical classes.&lt;/p&gt;

&lt;p&gt;Larson's article offers a multifaceted perspective on defining a technology company, suggesting that the answer is broader than a sole perspective. It involves a blend of litmus tests that encompasses economic models, strategic roles of engineering, and the potential for disruptive innovation.&lt;/p&gt;

</description>
      <category>technology</category>
      <category>finance</category>
      <category>career</category>
      <category>leadership</category>
    </item>
    <item>
      <title>Look Mum, No Servers! Create a Telegram Bot to communicate with GitHub Actions</title>
      <dc:creator>dennislwm</dc:creator>
      <pubDate>Fri, 23 Feb 2024 04:00:06 +0000</pubDate>
      <link>https://dev.to/dennislwm/look-mum-no-servers-create-a-telegram-bot-to-communicate-with-github-actions-1f40</link>
      <guid>https://dev.to/dennislwm/look-mum-no-servers-create-a-telegram-bot-to-communicate-with-github-actions-1f40</guid>
      <description>&lt;ul&gt;
&lt;li&gt;TL;DR&lt;/li&gt;
&lt;li&gt;
Essentials First

&lt;ul&gt;
&lt;li&gt;Crafting Your Telegram Bot&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;From DevOps to BotOps&lt;/li&gt;
&lt;li&gt;Deep dive into the GitHub Actions Configuration&lt;/li&gt;
&lt;li&gt;
Steps to Configure Your Pipedream Workflow

&lt;ul&gt;
&lt;li&gt;Step 1. &lt;strong&gt;Command Reception&lt;/strong&gt; : Set up a trigger for Telegram bot commands.&lt;/li&gt;
&lt;li&gt;Step 2. &lt;strong&gt;GitHub Interaction&lt;/strong&gt; : Automate file updates in your repository.&lt;/li&gt;
&lt;li&gt;Step 3. &lt;strong&gt;Delay for Action Completion&lt;/strong&gt; : Ensure synchronization with GitHub Actions.&lt;/li&gt;
&lt;li&gt;Step 4. &lt;strong&gt;Results Retrieval&lt;/strong&gt; : Fetch the solved puzzle from the repository.&lt;/li&gt;
&lt;li&gt;Step 5. &lt;strong&gt;Notification&lt;/strong&gt; : Inform the user about the solution directly through Telegram.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;li&gt;Get the Source Code&lt;/li&gt;
&lt;li&gt;What To Do Next&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://telegram.org"&gt;Telegram&lt;/a&gt; isn’t just for sending and receiving chat messages. It’s also for automating your dialog flow, including workflow.&lt;/p&gt;

&lt;p&gt;Using a Telegram Bot gives you the ability to check prices, query status, solve puzzles, and even have a fun conversation.&lt;/p&gt;

&lt;p&gt;And if you’re a serious developer or engineer, you can create your own Telegram Bot to manage your servers, view user details, and open or close issues.&lt;/p&gt;

&lt;p&gt;GitHub-Actions-Telegram-Bot is a Telegram bot that allow you to communicate with a GitHub Actions pipeline that may return an output message.&lt;/p&gt;




&lt;h2&gt;
  
  
  Essentials First
&lt;/h2&gt;

&lt;p&gt;Gear up by setting up accounts on (no credit card required):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://telegram.org"&gt;Telegram&lt;/a&gt; for instant messaging automation.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com"&gt;GitHub&lt;/a&gt; for repository management and actions.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pipedream.com"&gt;Pipedream&lt;/a&gt; for connecting apps and automating workflows.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Crafting Your Telegram Bot
&lt;/h3&gt;

&lt;p&gt;My previous article &lt;a href="https://dev.to/dennislwm/building-a-telegram-chat-with-a-mt4-forex-trading-expert-advisor-4p35"&gt;Building a Telegram Chat with a MT4 Forex Trading Expert Advisor&lt;/a&gt; contains a prerequisite tutorial on &lt;a href="https://github.com/dennislwm/MT4-Telegram-Bot-Recon"&gt;How to Create a New Telegram Bot&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s a goldmine for those into crypto or forex trading too.&lt;/p&gt;




&lt;h2&gt;
  
  
  From DevOps to BotOps
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions"&gt;GitHub Actions&lt;/a&gt;, a powerhouse for CI/CD, lets you automate builds, tests, and deployments. Integrating it with a Telegram Bot creates a dynamic duo for automating workflows beyond the traditional scope.&lt;/p&gt;

&lt;p&gt;A typical workflow for a CI/CD pipeline is to make some changes to your repository files, then add and commit these file changes. The pipeline may trigger on a &lt;code&gt;push&lt;/code&gt;, or any event types, that you have defined in a GitHub Actions config file.&lt;/p&gt;

&lt;p&gt;We can extend this CI/CD pipeline by creating a webhook that listens for a Telegram bot command to start a chain of events, which includes automating the CI/CD pipeline. The result of the pipeline may then be returned to the Telegram bot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--daEDWmjn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dennislwm.netlify.app/images/look-mum-no-servers/GitHub-Actions-Telegram-Bot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--daEDWmjn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dennislwm.netlify.app/images/look-mum-no-servers/GitHub-Actions-Telegram-Bot.png" alt="Overview" width="743" height="131"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article, you’ll have a bit of fun by creating a Telegram Bot to automate solving a sudoku puzzle and returning the result.&lt;/p&gt;

&lt;p&gt;You’ll send a command (prefix with a &lt;code&gt;/&lt;/code&gt;) to the Telegram Bot. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/solve PUZZLE_STRING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This triggers a chain of events, which includes automating the GitHub Actions pipeline, and the result is a text message reply that contains the solved puzzle.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RAe8HU8E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dennislwm.netlify.app/images/look-mum-no-servers/Sudoku-result.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RAe8HU8E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dennislwm.netlify.app/images/look-mum-no-servers/Sudoku-result.jpeg" alt="Sudoku result" width="296" height="640"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Deep dive into the GitHub Actions Configuration
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;TL;DR: You can fork the source code from my GitHub repository for a head start.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before diving into configuring the workflow, let’s take a peek at how the GitHub Actions configuration works.&lt;/p&gt;

&lt;p&gt;To create a GitHub Actions pipeline, we have to specify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name of the GitHub Actions &lt;code&gt;name&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Type of trigger events &lt;code&gt;on&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;An array of jobs &lt;code&gt;jobs&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;A base image &lt;code&gt;runs-on&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;An array of steps &lt;code&gt;steps&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Checkout git repo.&lt;/li&gt;
&lt;li&gt;Set up Python.&lt;/li&gt;
&lt;li&gt;Install dependencies.&lt;/li&gt;
&lt;li&gt;Run tests.&lt;/li&gt;
&lt;li&gt;Run application.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s look at an example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c4OHrJR---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dennislwm.netlify.app/images/look-mum-no-servers/GitHub-Actions-Config-File.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c4OHrJR---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dennislwm.netlify.app/images/look-mum-no-servers/GitHub-Actions-Config-File.png" alt="GitHub Actions Config File" width="800" height="663"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We name the GitHub Actions &lt;code&gt;Python application&lt;/code&gt;, and it will trigger on each &lt;code&gt;push&lt;/code&gt; to the &lt;code&gt;master&lt;/code&gt; branch.&lt;/p&gt;

&lt;p&gt;There is one job &lt;code&gt;build&lt;/code&gt; using the base image &lt;code&gt;ubuntu-latest&lt;/code&gt; that runs each of the steps listed above.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first step checks out the git repository, with a minimum commit history of &lt;code&gt;fetch-depth&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The second and third steps install &lt;code&gt;Python 3.9&lt;/code&gt; and the dependencies in &lt;code&gt;requirements.txt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The fourth and fifth steps run the unit tests and application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the final step, the command &lt;code&gt;sudoku&lt;/code&gt; reads the file &lt;code&gt;examples/puzzle.txt&lt;/code&gt; containing a PUZZLE_STRING. The return output string is then piped through a series of formatting commands using &lt;code&gt;sed&lt;/code&gt; and &lt;code&gt;awk&lt;/code&gt; and into the file &lt;code&gt;examples/result.txt&lt;/code&gt;. The &lt;code&gt;result.txt&lt;/code&gt; file is compared to the last commit and a new commit is created only if there is a change in the file.&lt;/p&gt;




&lt;h2&gt;
  
  
  Steps to Configure Your Pipedream Workflow
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;TL;DR: You can copy the configuration from my Pipedream workflow for swift integration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In order to create a webhook that listens for your Telegram bot commands, and starts a chain of events, which includes automating the CI/CD pipeline, you’ll need to create a workflow in &lt;a href="http://pipedream.com"&gt;Pipedream&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At a helicopter view, the steps to configure are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Step 1. &lt;strong&gt;Command Reception&lt;/strong&gt; : Set up a trigger for Telegram bot commands.&lt;/li&gt;
&lt;li&gt;Step 2. &lt;strong&gt;GitHub Interaction&lt;/strong&gt; : Automate file updates in your repository.&lt;/li&gt;
&lt;li&gt;Step 3. &lt;strong&gt;Delay for Action Completion&lt;/strong&gt; : Ensure synchronization with GitHub Actions.&lt;/li&gt;
&lt;li&gt;Step 4. &lt;strong&gt;Results Retrieval&lt;/strong&gt; : Fetch the solved puzzle from the repository.&lt;/li&gt;
&lt;li&gt;Step 5. &lt;strong&gt;Notification&lt;/strong&gt; : Inform the user about the solution directly through Telegram.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your TELEGRAM_BOT_TOKEN that you created in the previous section will come in handy here.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to your Pipedream dashboard &amp;gt; Projects.&lt;/li&gt;
&lt;li&gt;Click New Project, and name the project &lt;strong&gt;Telegram-Bot&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click Save, and your project should appear under Projects.&lt;/li&gt;
&lt;li&gt;Click on your project name, navigate to Resources.&lt;/li&gt;
&lt;li&gt;Click on New &amp;gt; Workflow, and name the workflow &lt;strong&gt;Sudoku-Actions&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click Create Workflow, and your workflow should appear under Resources.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 1. &lt;strong&gt;Command Reception&lt;/strong&gt; : Set up a trigger for Telegram bot commands.
&lt;/h3&gt;

&lt;p&gt;Now that you have created a workflow, let’s create a trigger.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on your workflow name, and search for &lt;strong&gt;Telegram&lt;/strong&gt; app.&lt;/li&gt;
&lt;li&gt;Select Telegram Bot &amp;gt; &lt;strong&gt;New Bot Command Received (Instant)&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;In the Telegram Bot Account, select &lt;strong&gt;Connect new account&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In token, enter your TELEGRAM_BOT_TOKEN.&lt;/li&gt;
&lt;li&gt;In nickname, enter your Telegram bot name.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Click Save, and your telegram bot should appear under Telegram Bot Account.&lt;/li&gt;
&lt;li&gt;Select Commands &amp;gt; and select one or more commands.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now to test your first action, open your Telegram app and send a command from your Telegram bot. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/solve 600009130700000090209500004926345000800006340473001000197004603302007000508900007
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tji7EgkP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dennislwm.netlify.app/images/look-mum-no-servers/New-Bot-Command-Received-from-Telegram-Bot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tji7EgkP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dennislwm.netlify.app/images/look-mum-no-servers/New-Bot-Command-Received-from-Telegram-Bot.png" alt="New Bot Command Received (Instant) from Telegram Bot" width="754" height="581"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2. &lt;strong&gt;GitHub Interaction&lt;/strong&gt; : Automate file updates in your repository.
&lt;/h3&gt;

&lt;p&gt;Now that you have created a trigger, let’s create some actions.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on the + icon (below your trigger), and search for &lt;strong&gt;GitHub&lt;/strong&gt; app.&lt;/li&gt;
&lt;li&gt;Select GitHub app, and search for &lt;strong&gt;file contents&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Create or update file contents&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;In the GitHub Account, select &lt;strong&gt;Connect new account&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Follow the steps to connect your GitHub account.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Click Save, and your account should appear under GitHub Account.&lt;/li&gt;
&lt;li&gt;Click Repository, search for and select &lt;strong&gt;sudoku-cli&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In Path, enter &lt;code&gt;examples/puzzle.txt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In File content, enter &lt;code&gt;{{steps.code.puzzle}}&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BtPlJ518--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dennislwm.netlify.app/images/look-mum-no-servers/Create-or-update-file-contents.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BtPlJ518--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dennislwm.netlify.app/images/look-mum-no-servers/Create-or-update-file-contents.png" alt="Create or update file contents" width="755" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3. &lt;strong&gt;Delay for Action Completion&lt;/strong&gt; : Ensure synchronization with GitHub Actions.
&lt;/h3&gt;

&lt;p&gt;As the GitHub Actions pipeline may take some time to execute, you will need to set a fixed period of delay in your workflow.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on the + icon (below your previous), and search for &lt;strong&gt;Workflow delay&lt;/strong&gt; app.&lt;/li&gt;
&lt;li&gt;Select Worflow delay app.&lt;/li&gt;
&lt;li&gt;In Duration to delay (value), enter &lt;code&gt;35&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In Duration to delay (unit), enter &lt;code&gt;seconds&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--obEFPrrN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dennislwm.netlify.app/images/look-mum-no-servers/Workflow-delay.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--obEFPrrN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dennislwm.netlify.app/images/look-mum-no-servers/Workflow-delay.png" alt="Workflow delay" width="755" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4. &lt;strong&gt;Results Retrieval&lt;/strong&gt; : Fetch the solved puzzle from the repository.
&lt;/h3&gt;

&lt;p&gt;The next step is to get the result from the repository.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on the + icon (below your trigger), and search for &lt;strong&gt;GitHub&lt;/strong&gt; app.&lt;/li&gt;
&lt;li&gt;Select GitHub app, and search for &lt;strong&gt;get repository&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Get repository content&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click Repository, search for and select &lt;strong&gt;sudoku-cli&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In Path, enter &lt;code&gt;examples/result.txt&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VTewD2FL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dennislwm.netlify.app/images/look-mum-no-servers/Get-repository-content.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VTewD2FL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dennislwm.netlify.app/images/look-mum-no-servers/Get-repository-content.png" alt="Get repository content" width="756" height="559"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5. &lt;strong&gt;Notification&lt;/strong&gt; : Inform the user about the solution directly through Telegram.
&lt;/h3&gt;

&lt;p&gt;The final step is to send a text message to your Telegram bot.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on the + icon (below your trigger), and search for &lt;strong&gt;Telegram&lt;/strong&gt; app.&lt;/li&gt;
&lt;li&gt;Select Telegram app, and search for &lt;strong&gt;send text&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Send text message or reply&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click Telegram Bot Account, search for and select &lt;strong&gt;sudokuclibot&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In Path, enter &lt;code&gt;{{steps.trigger.event.message.chat.id}}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In Text, enter &lt;code&gt;{{steps.base64_decode_string.data}}&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--D_aVyziO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dennislwm.netlify.app/images/look-mum-no-servers/Send-text-message-or-reply.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D_aVyziO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dennislwm.netlify.app/images/look-mum-no-servers/Send-text-message-or-reply.png" alt="Final step" width="755" height="585"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, you created a GitHub Actions to run a Python application &lt;code&gt;sudoku&lt;/code&gt; that accepts a file containing a PUZZLE_STRING, and returns the result in an output file &lt;code&gt;result.txt&lt;/code&gt;. You then created a Pipedream workflow that listens to your Telegram Bot for a given command &lt;code&gt;/solve PUZZLE_STRING&lt;/code&gt; that starts a chain of events, which includes automating the CI/CD pipeline. The workflow then replies to your Telegram Bot with the contents of the file &lt;code&gt;result.txt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By creating a bot that automates tasks and streamlines workflows, you’ve taken a significant step towards adopting modern DevOps practices.&lt;/p&gt;




&lt;h2&gt;
  
  
  Get the Source Code
&lt;/h2&gt;

&lt;p&gt;You can copy the above configuration from my Pipedream workflow &lt;a href="https://pipedream.com/new?h=tch_egfA5N"&gt;Telegram Bot &amp;gt; Sudoku-Actions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can download the above source code from my GitHub repository &lt;a href="https://github.com/dennislwm/sudoku-cli"&gt;dennislwm/sudoku-cli&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What To Do Next
&lt;/h2&gt;

&lt;p&gt;You can elevate your bot’s capabilities by:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Minimizing Wait Times&lt;/strong&gt; : Streamline results delivery by integrating direct feedback mechanisms from GitHub Actions to your Telegram bot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expanding Functionality&lt;/strong&gt; : You can enrich your bot with new commands for diverse tasks, such as transcribe a YouTube video, check next T-Bills auction dates, etc.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Embrace this journey to automate and innovate, shaping the future of how we interact with technology.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Was this article useful? Help me to improve by replying in the comments.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/dennislwm"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7uiU5Oz8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://img.shields.io/badge/buy%2520me%2520a%2520coffee-donate-yellow.svg" alt="Buy Me A Coffee donate button" width="146" height="20"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>githubactions</category>
      <category>bot</category>
    </item>
    <item>
      <title>Comparison of YouTube Free Autogenerated Subtitles vs Paid AI</title>
      <dc:creator>dennislwm</dc:creator>
      <pubDate>Fri, 19 Jan 2024 04:00:06 +0000</pubDate>
      <link>https://dev.to/dennislwm/comparison-of-youtube-free-autogenerated-subtitles-vs-paid-ai-1mm0</link>
      <guid>https://dev.to/dennislwm/comparison-of-youtube-free-autogenerated-subtitles-vs-paid-ai-1mm0</guid>
      <description>&lt;ul&gt;
&lt;li&gt;TL;DR&lt;/li&gt;
&lt;li&gt;Prerequisites&lt;/li&gt;
&lt;li&gt;Create a process to download and convert an autogenerated subtitle file from YouTube&lt;/li&gt;
&lt;li&gt;Download an audio file from YouTube and import it into Notta&lt;/li&gt;
&lt;li&gt;Comparison of YouTube vs Notta AI&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;li&gt;Get the Source Code&lt;/li&gt;
&lt;li&gt;What To Do Next&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://youtube.com"&gt;YouTube&lt;/a&gt; isn’t just for watching and sharing videos. It’s also for listening to podcasts, and reading transcriptions. Using an open-source application gives you the ability to download the video, audio or text data only, where you can consume the data at your own leisure, or even autogenerate blog articles.&lt;/p&gt;

&lt;p&gt;In this first part of a multi-series blog you’ll use a command-line interface (CLI) &lt;code&gt;yt-dlp&lt;/code&gt; to download YouTube’s free autogenerated subtitles and compare it to paid AI transcription services, such as &lt;a href="https://openai.com"&gt;OpenAI&lt;/a&gt;, &lt;a href="https://notta.ai"&gt;Notta.ai&lt;/a&gt;, and &lt;a href="https://otter.ai"&gt;Otter.ai&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An open-source CLI &lt;a href="https://github.com/yt-dlp/yt-dlp"&gt;yt-dlp&lt;/a&gt; to download YouTube files.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.python.org"&gt;Python 3&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://notta.ai"&gt;Notta.ai&lt;/a&gt; account (no credit card required).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Create a process to download and convert an autogenerated subtitle file from YouTube
&lt;/h2&gt;

&lt;p&gt;In order to design a workflow to autogenerate blog articles, we must first create a process to download a subtitle file from YouTube.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open a terminal and run the command &lt;code&gt;yt-dlp --version&lt;/code&gt; to check if it is installed.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2023.10.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Type the following command to download the autogenerated subtitle file of a YouTube video. Replace the YOUTUBE_URL with any YouTube link, e.g. &lt;code&gt;https://www.youtube.com/watch?v=x3vnCKivCjs&lt;/code&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yt-dlp --write-auto-sub --skip-download [YOUTUBE_URL]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see an output similar to below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[youtube] Extracting URL: https://www.youtube.com/watch?v=x3vnCKivCjs
[youtube] x3vnCKivCjs: Downloading webpage
[youtube] x3vnCKivCjs: Downloading ios player API JSON
[youtube] x3vnCKivCjs: Downloading android player API JSON
[youtube] x3vnCKivCjs: Downloading m3u8 information
[info] x3vnCKivCjs: Downloading subtitles: en
[info] x3vnCKivCjs: Downloading 1 format(s): 22
[info] Writing video subtitles to: The Fastest Way to Lose Belly Fat [x3vnCKivCjs].en.vtt
[download] Destination: The Fastest Way to Lose Belly Fat [x3vnCKivCjs].en.vtt
[download] 100% of 85.84KiB in 00:00:00 at 879.88KiB/s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The subtitle file name is created using the video title and code, e.g. &lt;code&gt;The Fastest Way to Lose Belly Fat [x3vnCKivCjs].en.vtt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The reason that you’ll need to convert the subtitle file is if you open the &lt;code&gt;vtt&lt;/code&gt; file, it contains metadata that makes it hard to read.&lt;/p&gt;

&lt;p&gt;Fortunately, there is a &lt;a href="https://gist.github.com/glasslion/b2fcad16bc8a9630dbd7a945ab5ebf5e"&gt;Python script&lt;/a&gt; that converts youtube subtitle file (&lt;code&gt;vtt&lt;/code&gt;) to plain text. Credit to &lt;a href="https://gist.github.com/glasslion"&gt;glasslion&lt;/a&gt; for making it open-source.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Download the above Python script &lt;code&gt;vtt2text.py&lt;/code&gt; into the same folder as your &lt;code&gt;vtt&lt;/code&gt; file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In your terminal, type the following command.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python vtt2text.py The\ Fastest\ Way\ to\ Lose\ Belly\ Fat\ \[x3vnCKivCjs\].en.vtt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;If successful, you’ll find a new &lt;code&gt;txt&lt;/code&gt; file created, e.g. &lt;code&gt;The Fastest Way to Lose Belly Fat [x3vnCKivCjs].en.txt&lt;/code&gt;, which has readable text but still contains a few metadata lines.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00:00
today I'm going to share with you the absolute fastest way
to lose your belly now you could have the best willpower
the best discipline really want it really bad and never
really see any results because you're missing the technique
you're missing the strategy I'm the perfect example I took
guitar lessons for six years right as a teenager and I
never really progressed or never really went anywhere
because the techniques that were taught to me were just not
that great great the same thing happened with tennis in
college I was never taught the right technique and so I
would use force and I would keep practicing but practice
incorrectly never went anywhere and this really applies
with weight loss too because if you have the right way of
doing something you don't have to put so much effort into
it so what I'm going to show you is the fastest way to
achieve your goal number one when you do the ketogenic diet
it's not differentiated what type of fats you should be
eating in other words keto is really about low carb you
...

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: You’ll need to tweak the Python script to remove the metadata lines. If you prefer to download my enhanced Python script, you can Get the Source Code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Download an audio file from YouTube and import it into Notta
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Type the following command to download the audio data of a YouTube video. Replace the YOUTUBE_URL with any YouTube link, e.g. &lt;code&gt;https://www.youtube.com/watch?v=x3vnCKivCjs&lt;/code&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yt-dlp -f m4a --quiet [YOUTUBE_URL]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The audio file name is created using the video title and code, e.g. &lt;code&gt;The Fastest Way to Lose Belly Fat [x3vnCKivCjs].en.m4a&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Open a browser, navigate to &lt;a href="https://notta.ai"&gt;Notta.ai&lt;/a&gt; and login to your account.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the Dashboard, click on &lt;code&gt;Import files&lt;/code&gt;, and select the audio file above.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If successful, you should see a new recording under &lt;strong&gt;Recent recordings&lt;/strong&gt; in your Dashboard.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on the recording for your audio, e.g. &lt;code&gt;The Fastest Way to Lose Belly Fat [x3vnCKivCjs]&lt;/code&gt;, to see the output transcript.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Notta’s output transcript is segregated into blocks based on speakers.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Today, I'm going to share with you the absolute fastest way to lose your belly. Now, you can have the best willpower, the best discipline, really want it, really bad, and never really see any results because you're missing the technique.

You're missing the strategy. I'm the perfect example. I took guitar lessons for six years as a teenager, and I never really progressed or never really went anywhere because the techniques that were taught to me were just not that great.

The same thing happened with tennis in college. I was never taught the right technique, and so I would use force, and I would keep practicing but practice incorrectly. Never went anywhere. This really applies with weight loss too because if you have the right way of doing something, you don't have to put so much effort into it.

What I'm going to show you is the fastest way to achieve your goal. Number one, when you do the ketogenic diet, it's not differentiated what type of fats you should be eating. In other words, keto is really about low carb.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Comparison of YouTube vs Notta AI
&lt;/h2&gt;

&lt;p&gt;Let’s start with the negatives.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;YouTube free autogenerated subtitles&lt;/th&gt;
&lt;th&gt;Notta AI transcription services&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1. Must be familiar or comfortable using the command-line.&lt;/td&gt;
&lt;td&gt;1. Paid service, but offer a free tier of 120 mins.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2. Two-step process of downloading the subtitle and converting it to text.&lt;/td&gt;
&lt;td&gt;2. Two-step process of downloading an audio file and importing it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3. The converted text has a few lines of metadata.&lt;/td&gt;
&lt;td&gt;3. Most features are behind a paywall, e.g. view the first 5 mins of transcript.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4. The converted text has no punctuations.&lt;/td&gt;
&lt;td&gt;4. No API or SDK to allow the creation of an automated pipeline.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For the positives.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;YouTube free autogenerated subtitles&lt;/th&gt;
&lt;th&gt;Paid AI transcription services&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1. You can create a workflow of automated processes using a pipeline.&lt;/td&gt;
&lt;td&gt;1. The converted text has punctuations and proper sentences.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2. No subscription cost, only your Internet bandwidth for downloading the subtitles.&lt;/td&gt;
&lt;td&gt;2. You can create a custom template for your converted text.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both YouTube and Notta seem to have about the same accuracy based on the sample output text, however you’ll have to find an application to measure the accuracy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The free YouTube autogenerated subtitle is a good option for personal use, such as generating transcription for offline reading. However, due to the lack of polish and also customisable templates, the Paid AI transcription services should be considered for commercial use, which may include autogenerating articles for your blog.&lt;/p&gt;




&lt;h2&gt;
  
  
  Get the Source Code
&lt;/h2&gt;

&lt;p&gt;You can download the above source code from my GitHub repository &lt;a href="https://github.com/dennislwm/playscribe?tab=readme-ov-file#62-convert-a-subtitle-file-to-text"&gt;dennislwm/playscribe&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What To Do Next
&lt;/h2&gt;

&lt;p&gt;You can further extend your code in several meaningful ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Implement a GitHub Actions that will continuously monitor an RSS feed for new YouTube links, and process the links and returns the transcribe results.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implement a static site blog that publishes each transcribe result as a new post, with an RSS feed to allow users to subscribe to new posts.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Was this article useful? Help me to improve by replying in the comments.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/dennislwm"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7uiU5Oz8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://img.shields.io/badge/buy%2520me%2520a%2520coffee-donate-yellow.svg" alt="Buy Me A Coffee donate button" width="146" height="20"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>youtube</category>
      <category>ai</category>
      <category>videoapi</category>
    </item>
    <item>
      <title>Custom GitLab Runner to run any CI/CD pipeline on your workstation</title>
      <dc:creator>dennislwm</dc:creator>
      <pubDate>Thu, 10 Nov 2022 02:51:18 +0000</pubDate>
      <link>https://dev.to/dennislwm/custom-gitlab-runner-to-run-any-cicd-pipeline-on-your-workstation-5h9j</link>
      <guid>https://dev.to/dennislwm/custom-gitlab-runner-to-run-any-cicd-pipeline-on-your-workstation-5h9j</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;TL;DR&lt;/li&gt;
&lt;li&gt;Homebrew Installation&lt;/li&gt;
&lt;li&gt;Registering Runners&lt;/li&gt;
&lt;li&gt;Adhoc Analytics&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I wanted to find any hidden constraints when registering custom runners with GitLab.com, specifically on its free tier. I'm happy to state that the benefits of using custom runners far outweigh the effort required to setup on your workstation.&lt;/p&gt;

&lt;p&gt;Before using custom runners, my average CI/CD usage per month was 67 minutes, with a maximum of 146 minutes. Although my usage was way below the 400 free CI/CD minutes per month on GitLab.com, however, using custom runners has been liberating for me as I don't have to worry about paying GitLab.com to get additional minutes in the future.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdennislwm.netlify.app%2Fimages%2Fcustom-gitlab-runner-to-run-any-cicd-pipeline-on-your-workstation%2Fusage-quota.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdennislwm.netlify.app%2Fimages%2Fcustom-gitlab-runner-to-run-any-cicd-pipeline-on-your-workstation%2Fusage-quota.png" alt="Usage Quotas"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;You can register multiple runners on the same host machine, each with a different configuration, by repeating the &lt;code&gt;register&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;After registering a custom runner, GitLab.com continues consuming any unused free CI/CD minutes because there isn't any way to configure GitLab runner priority according to this &lt;a href="https://gitlab.com/gitlab-org/gitlab/-/issues/14976" rel="noopener noreferrer"&gt;open issue&lt;/a&gt;. Fortunately, there is a workaround, where you can specify tags in both your runner and CI/CD pipeline.&lt;/p&gt;

&lt;p&gt;When you register a runner, you can specify the runner's tags, for example &lt;code&gt;my-runner&lt;/code&gt;. To pick up and run a job, a runner must be assigned every tag listed in the CI/CD configuration. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;my-runner&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://docs.gitlab.com/ee/ci/yaml/index.html#tags" rel="noopener noreferrer"&gt;GitLab's tags keyword reference&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Homebrew Installation
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Install GitLab Runner with &lt;code&gt;brew install gitlab-runner&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install GitLab Runner as a service with &lt;code&gt;brew services start gitlab-runner&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can verify that GitLab Runner created the service with &lt;code&gt;brew services list&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Name          Status  User      File
gitlab-runner started dennislwm ~/Library/LaunchAgents/homebrew.mxcl.gitlab-runner.plist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Installing the GitLab Runner as a service will ensure that your runner is available each time you restart your workstation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Registering Runners
&lt;/h2&gt;

&lt;p&gt;You can register multiple runners on the same host machine, each with a different configuration, by repeating the &lt;code&gt;gitlab-runner register&lt;/code&gt; command, which will be followed by an interactive session.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Enter the GitLab instance URL, which can be either managed or self-hosted. For example, the managed instance URL is &lt;code&gt;https://gitlab.com/&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enter the registration token for either:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;a shared runner (not available on GitLab.com) - go to &lt;strong&gt;GitLab Admin Area &amp;gt; Overview &amp;gt; Runners&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a group runner (your custom runner will be available to all projects within this group) - go to &lt;strong&gt;Group CI/CD &amp;gt; Runners&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a project runner - go to &lt;strong&gt;Project Settings &amp;gt; CI/CD &amp;gt; Runners&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Enter a description for the runner, to distinguish multipler runners, e.g. &lt;code&gt;my-runner-01&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enter comma-separated tags for the runner (your CI/CD configuration must have the same tags as your custom runner in order for the runner to pick up and run the job), e.g. &lt;code&gt;my-runner&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enter optional maintenance note for the runner, you may leave as blank.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enter an executor, e.g. &lt;code&gt;docker&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enter the default Docker image, e.g. &lt;code&gt;ubuntu&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After completing the above interactive session, you should see a message &lt;code&gt;Runner registered successfully&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Adhoc Analytics
&lt;/h2&gt;

&lt;p&gt;Basically, I checked the duration of the same job when using a shared runner vs a custom runner. A shared runner took 1 minute 41 seconds, while a custom runner took 1 minute 14 seconds, which is about 25% faster than a shared runner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;There was no major constraint when registering custom runners with managed GitLab.com. &lt;/p&gt;

&lt;p&gt;The benefits of using a custom runner are you do not have to pay for additional CI/CD minutes and it is faster than a shared runner when picking up and running a job.&lt;/p&gt;

</description>
      <category>gitlab</category>
      <category>cicd</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Deploy a WordPress pipeline to minimize and upload images</title>
      <dc:creator>dennislwm</dc:creator>
      <pubDate>Sun, 14 Nov 2021 04:00:06 +0000</pubDate>
      <link>https://dev.to/dennislwm/deploy-a-wordpress-pipeline-to-minimize-and-upload-images-28k2</link>
      <guid>https://dev.to/dennislwm/deploy-a-wordpress-pipeline-to-minimize-and-upload-images-28k2</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;
Deep dive into the &lt;code&gt;curl&lt;/code&gt; command

&lt;ul&gt;
&lt;li&gt;Limitation of &lt;code&gt;curl&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Create a deployable Shell script to minimize and upload images

&lt;ul&gt;
&lt;li&gt;Requirement&lt;/li&gt;
&lt;li&gt;Understand function &lt;code&gt;wp-upload&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Execute command &lt;code&gt;wp-upload&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Debug function &lt;code&gt;wp-upload&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Source code &lt;code&gt;wprc.sh&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Generate a WordPress application username and password

&lt;ul&gt;
&lt;li&gt;Requirement&lt;/li&gt;
&lt;li&gt;WordPress REST API&lt;/li&gt;
&lt;li&gt;
Allow Specific Users to the WP REST API

&lt;ul&gt;
&lt;li&gt;Install Application Passwords Plugin&lt;/li&gt;
&lt;li&gt;Test Specific User Access to the WP REST API&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;li&gt;What To Do Next&lt;/li&gt;
&lt;li&gt;
Troubleshooting

&lt;ul&gt;
&lt;li&gt;Error Access to REST API requests is restricted by iThemes Security settings.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;There are several limitations when using a shared &lt;strong&gt;WordPress (WP)&lt;/strong&gt; hosting. If you have used &lt;code&gt;wp-cli&lt;/code&gt;, you would have known that you can perform most of the WP functions, such as publishing a new post, uploading media, etc from your local terminal, which saves a lot of time.&lt;/p&gt;

&lt;p&gt;However, &lt;code&gt;wp-cli&lt;/code&gt; requires you to have administrator access on the host computer, which is not usually granted to shared hosting users. This means that you have to perform most of the WP functions from the &lt;strong&gt;WordPress&lt;/strong&gt; web UI, which usually takes longer to navigate and process due to the graphical load.&lt;/p&gt;

&lt;p&gt;What is a better solution for web administrators of a shared &lt;strong&gt;WordPress&lt;/strong&gt; hosting?&lt;/p&gt;

&lt;p&gt;The answer is to deploy a pipeline that can perform a single WP function, such as creating a new post, or a combination, such as minimizing and uploading images, using shell scripts and the &lt;strong&gt;WordPress&lt;/strong&gt; RESTful APIs.&lt;/p&gt;

&lt;p&gt;In this tutorial, you will use a Shell function to minimize multiple images and upload them to your shared &lt;strong&gt;WordPress&lt;/strong&gt; account. You’ll use the &lt;code&gt;id&lt;/code&gt; of each image to perform further processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deep dive into the &lt;code&gt;curl&lt;/code&gt; command
&lt;/h2&gt;

&lt;p&gt;Before diving into deploying the pipeline, let’s take a peek at how the WordPress RESTful API works, in particular the REST API &lt;strong&gt;Media&lt;/strong&gt; base route &lt;code&gt;/wp-json/wp/v2/media&lt;/code&gt;, with the &lt;code&gt;curl&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;See below to take a &lt;strong&gt;deep dive into curl&lt;/strong&gt; command.  &lt;/p&gt;

&lt;p&gt;To upload an image to WordPress with &lt;code&gt;curl&lt;/code&gt;, we have to specify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RESTful API method &lt;code&gt;-X&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;RESTful API endpoint URL &lt;code&gt;--url&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;path of image file &lt;code&gt;--data-binary&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;multiple headers each with &lt;code&gt;-H&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;parameter &lt;code&gt;--location&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s look at an example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y_NvENRw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dennislwm.netlify.app/images/deploy-a-wordpress-pipeline-to-minimize-and-upload-images/curl-example.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y_NvENRw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dennislwm.netlify.app/images/deploy-a-wordpress-pipeline-to-minimize-and-upload-images/curl-example.png" alt="curl command to upload an image" width="880" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We use the RESTful API method &lt;code&gt;-X POST&lt;/code&gt; because we are updating the image to the WordPress database.&lt;/p&gt;

&lt;p&gt;The RESTful API endpoint &lt;code&gt;--url&lt;/code&gt; is your domain name, e.g &lt;code&gt;MYAWESOMEDOMAIN.COM&lt;/code&gt; followed by &lt;code&gt;/wp-json/wp/v2/media&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The path of image file &lt;code&gt;--data-binary&lt;/code&gt; has to be prefixed by &lt;code&gt;@&lt;/code&gt;, e.g. &lt;code&gt;"@/path/to/file.png"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The header &lt;code&gt;-H&lt;/code&gt; for &lt;code&gt;content-disposition&lt;/code&gt; requires a &lt;code&gt;filename=file.png&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The header &lt;code&gt;-H&lt;/code&gt; for &lt;code&gt;authorization&lt;/code&gt; requires a string token from your Wordpress application &lt;code&gt;USERNAME:PASSWORD&lt;/code&gt; string combination. Execute the command &lt;code&gt;echo -n "USERNAME:PASSWORD" | base64&lt;/code&gt; to generate an output string token, e.g. &lt;code&gt;VVNFUk5BTUU6UEFTU1dPUkQ=&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The header &lt;code&gt;-H&lt;/code&gt; for &lt;code&gt;cache-control&lt;/code&gt; disables cache, while the header &lt;code&gt;-H&lt;/code&gt; for &lt;code&gt;content-type&lt;/code&gt; specifies the image type, e.g. &lt;code&gt;image/png&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And finally, the parameter &lt;code&gt;--location&lt;/code&gt; will make &lt;code&gt;curl&lt;/code&gt; redo the request if the requested page has moved to a new location.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitation of &lt;code&gt;curl&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The limitation of using &lt;code&gt;curl&lt;/code&gt; command when uploading an image file is that it only allows one file per request. Ideally, we want to be able to specify multiple files using wildcards within a single command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a deployable Shell script to minimize and upload images
&lt;/h2&gt;

&lt;p&gt;In this section, let’s create a Shell script in any text editor, and name the file &lt;code&gt;wprc.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This section requires both the WordPress application &lt;code&gt;USERNAME&lt;/code&gt; and &lt;code&gt;PASSWORD&lt;/code&gt;. If you don’t have these, you can generate them in the next section Generate a WordPress application username and password.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirement
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;WordPress application &lt;code&gt;USERNAME&lt;/code&gt; and &lt;code&gt;PASSWORD&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Shell terminal, e.g. &lt;code&gt;bash&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Shell commands &lt;code&gt;curl&lt;/code&gt; and &lt;code&gt;base64&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Any text editor&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Understand function &lt;code&gt;wp-upload&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Open the Shell file &lt;code&gt;wprc.sh&lt;/code&gt;, and let’s write a function to encapsulate the &lt;code&gt;curl&lt;/code&gt; command, such as &lt;code&gt;wp-upload()&lt;/code&gt;. This function loops through all files found within a given path, and executes the &lt;code&gt;curl&lt;/code&gt; command for each file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wp-upload: Minimizes and uploads image(s) to WordPress
Usage: [WP_DEBUG=false] wp-upload [WP_PATH]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function accepts one parameter, which is the given path the the image files, e.g. &lt;code&gt;/path/to&lt;/code&gt;. We should assert that at least one valid file in the path before running the loop.&lt;/p&gt;

&lt;p&gt;The onus is on the user to ensure that all files in the given path are image files, e.g. &lt;code&gt;jpeg&lt;/code&gt; or &lt;code&gt;png&lt;/code&gt;, that are supported by the HTTP &lt;code&gt;application/type&lt;/code&gt;. For example &lt;code&gt;image/jpeg&lt;/code&gt; is supported, but not &lt;code&gt;jpg&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Some common image types are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;image/apng&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;image/avif&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;image/gif&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;image/jpeg&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;image/png&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;image/svg+xml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;image/webp&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Shell method extracts and sets the &lt;code&gt;image/TYPE&lt;/code&gt; from the extension of each file, e.g. &lt;code&gt;filename.jpeg&lt;/code&gt;. If the image type is invalid, then the &lt;code&gt;curl&lt;/code&gt; command will fail. Hence, the user has to ensure that the extension of each file corresponds to a valid image type, e.g. &lt;code&gt;filename.svg+xml&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Execute command &lt;code&gt;wp-upload&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The environment variables &lt;code&gt;WP_USERNAME&lt;/code&gt; and &lt;code&gt;WP_PASSWORD&lt;/code&gt; are required for WordPress authentication. You can save these variables in a separate file, one variable per line, such as &lt;code&gt;env.sh&lt;/code&gt; and load the variables with &lt;code&gt;source env.sh&lt;/code&gt; before running the function.&lt;/p&gt;

&lt;p&gt;Alternatively, you can set these variables at each command as follows:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ WP_USERNAME=USERNAME WP_PASSWORD=PASSWORD wp-upload&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The function prompts for a user confirmation before executing the &lt;code&gt;curl&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  WP_PATH=/Users/dennislwm/fx-git-pull/MYAWESOMEDOMAIN.COM/minify
Upload 2 image(s) to WordPress? 
Enter yes to confirm; OR BLANK to quit: yes

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only accepted value is &lt;code&gt;yes&lt;/code&gt;, as all other values will terminate the function. For each file upload that succeeds, the media &lt;code&gt;id&lt;/code&gt; is returned, e.g. &lt;code&gt;5873&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Uploading BlessingsInHarmony.jpeg
  % Total % Received % Xferd Average Speed Time Time Time Current
                                 Dload Upload Total Spent Left Speed
100 275k 0 4243 100 271k 1731 110k 0:00:02 0:00:02 --:--:-- 112k
5873
Uploading Order_Xmas T-Shirt.png
  % Total % Received % Xferd Average Speed Time Time Time Current
                                 Dload Upload Total Spent Left Speed
100 760k 0 4213 100 756k 974 175k 0:00:04 0:00:04 --:--:-- 176k
5874
done

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Debug function &lt;code&gt;wp-upload&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The function has a debug mode &lt;code&gt;WP_DEBUG=true&lt;/code&gt; that prints the &lt;code&gt;curl&lt;/code&gt; command for each file without executing it.&lt;/p&gt;

&lt;p&gt;See below to &lt;strong&gt;debug function wp-upload&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ WP_DEBUG=true wp-upload&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The result is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Uploading BlessingsInHarmony.jpeg
curl -X POST --url https://MYAWESOMEDOMAIN.COM/wp-json/wp/v2/media --data-binary "@/Users/dennislwm/fx-git-pull/MYAWESOMEDOMAIN.COM/minify/BlessingsInHarmony.jpeg" -H "content-disposition: attachment; filename=BlessingsInHarmony.jpeg" -H "authorization: Basic VVNFUk5BTUU6UEFTU1dPUkQ=" -H "cache-control: no-cache" -H "content-type: image/jpeg" --location
Uploading Order_Xmas T-Shirt.png
curl -X POST --url https://MYAWESOMEDOMAIN.COM/wp-json/wp/v2/media --data-binary "@/Users/dennislwm/fx-git-pull/MYAWESOMEDOMAIN.COM/minify/Order_Xmas T-Shirt.png" -H "content-disposition: attachment; filename=Order_Xmas T-Shirt.png" -H "authorization: Basic VVNFUk5BTUU6UEFTU1dPUkQ=" -H "cache-control: no-cache" -H "content-type: image/png" --location
done

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Source code &lt;code&gt;wprc.sh&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The source code of &lt;code&gt;wprc.sh&lt;/code&gt; contains both the functions &lt;code&gt;wp-upload&lt;/code&gt; and &lt;code&gt;inp-confirm&lt;/code&gt;. Before running these commands, you have to load the file with &lt;code&gt;source wprc.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;See below for the &lt;strong&gt;source code wprc.sh&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wp-upload() {
    cancel=true
    echo "wp-upload: Minimizes and uploads image(s) to WordPress"
    echo "Usage: [WP_DEBUG=$WP_DEBUG] wp-upload [WP_PATH]"
    echo "Input:"
    echo " [WP_PATH]: /path/to/image (default: /Users/dennislwm/fx-git-pull/MYAWESOMEDOMAIN.COM/minify)"

    WP_PATH="/Users/dennislwm/fx-git-pull/MYAWESOMEDOMAIN.COM/minify"
    if [! -z "$1"]; then
        WP_PATH=$1
    fi
    WP_TOTAL=$( ls -lAd "$WP_PATH"/* | wc -l | xargs )
    echo " WP_PATH=$WP_PATH"
    echo "Upload $WP_TOTAL image(s) to WordPress? "

    confirm=$( inp-confirm )
    if ["$confirm" = "yes"]; then
        cancel=false
        for file in "$WP_PATH"/*; do
            wp_name=$( basename "$file" )
            wp_ext="${wp_name##*.}"
            echo "Uploading $wp_name"

            wp_data=$( printf '"@%s"' "$file" )
            wp_token=$( echo -n "$WP_USERNAME:$WP_PASSWORD" | base64 )
            wp_header1="$( printf '"content-disposition: attachment; filename=%s"' "$( basename "$wp_name" )" )"
            wp_header2="$( printf '"authorization: Basic %s"' "$wp_token" )"
            wp_header3="$( printf '"cache-control: no-cache"' )"
            wp_header4="$( printf '"content-type: image/%s"' "$wp_ext" )"
            if ["$WP_DEBUG" = "true"]; then
                echo curl -X POST --url https://MYAWESOMEDOMAIN.COM/wp-json/wp/v2/media --data-binary "$wp_data" -H "$wp_header1" -H "$wp_header2" -H "$wp_header3" -H "$wp_header4" --location
            else
                eval curl -X POST --url https://MYAWESOMEDOMAIN.COM/wp-json/wp/v2/media --data-binary "$wp_data" -H "$wp_header1" -H "$wp_header2" -H "$wp_header3" -H "$wp_header4" --location | jq ".id"
            fi
        done
    fi
    if $cancel; then
        echo "user cancel"
    else
        echo "done"
    fi
}

inp-confirm() {
    read -p "Enter yes to confirm; OR BLANK to quit: " name
    if [-z $name]; then
        echo ""
    else
        echo $name
    fi
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Generate a WordPress application username and password
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Requirement
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WordPress&lt;/strong&gt; GUI admin access&lt;/li&gt;
&lt;li&gt;WordPress &lt;strong&gt;iThemes Security&lt;/strong&gt; plugin&lt;/li&gt;
&lt;li&gt;WordPress &lt;strong&gt;Application Passwords&lt;/strong&gt; plugin&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  WordPress REST API
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;iThemes Security&lt;/strong&gt; plugin offers a setting to &lt;strong&gt;Restrict Access&lt;/strong&gt; to most REST API data.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;To activate the setting, navigate to &lt;code&gt;Security&lt;/code&gt; &amp;gt; &lt;code&gt;Settings&lt;/code&gt; &amp;gt; &lt;code&gt;WordPress Tweaks&lt;/code&gt; &amp;gt; &lt;code&gt;Configure Settings&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scroll to the &lt;strong&gt;REST API&lt;/strong&gt; section, and select the &lt;code&gt;Restricted Access&lt;/code&gt; setting.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The plugin can only enable/disable access to &lt;em&gt;everyone&lt;/em&gt; at once, however, it’s not possible to allow specific users to access the REST API.&lt;/p&gt;

&lt;h4&gt;
  
  
  Allow Specific Users to the WP REST API
&lt;/h4&gt;

&lt;h5&gt;
  
  
  Install Application Passwords Plugin
&lt;/h5&gt;

&lt;p&gt;In order to allow a specific user (or application) to access the REST API, you must install the &lt;strong&gt;Application Passwords&lt;/strong&gt; plugin.&lt;/p&gt;

&lt;p&gt;See below to &lt;strong&gt;install Application Passwords plugin&lt;/strong&gt;.  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to &lt;code&gt;Plugins&lt;/code&gt; &amp;gt; &lt;code&gt;Add New&lt;/code&gt; &amp;gt; Type &lt;code&gt;Application Passwords&lt;/code&gt; in the Search box &amp;gt; Press &lt;code&gt;Enter&lt;/code&gt; key &amp;gt; Click &lt;code&gt;Install Now&lt;/code&gt; on the plugin.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To allow a specific user, navigate to &lt;code&gt;Users&lt;/code&gt; &amp;gt; &lt;code&gt;All Users&lt;/code&gt; &amp;gt; Click &lt;code&gt;Edit&lt;/code&gt; for the &lt;code&gt;USERNAME&lt;/code&gt; that you want to enable access.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scroll to the &lt;strong&gt;Application Passwords&lt;/strong&gt; section, and type a new &lt;strong&gt;Application Name&lt;/strong&gt; and Click &lt;code&gt;Add New&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy down the generated password, e.g. &lt;code&gt;S5hd Tp3K GdhU GFXd zJhQ Wtwa&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jSCOGYmX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dennislwm.netlify.app/images/deploy-a-wordpress-pipeline-to-minimize-and-upload-images/application-passwords.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jSCOGYmX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dennislwm.netlify.app/images/deploy-a-wordpress-pipeline-to-minimize-and-upload-images/application-passwords.png" alt="generate a new application password" width="840" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Test Specific User Access to the WP REST API
&lt;/h5&gt;

&lt;p&gt;The WP REST API &lt;strong&gt;Media&lt;/strong&gt; base route is &lt;code&gt;/wp-json/wp/v2/media&lt;/code&gt;. This &lt;a href="https://developer.wordpress.org/rest-api/reference"&gt;API Reference&lt;/a&gt; provides information about the specific endpoints available through the API, their parameters, and their response data format.&lt;/p&gt;

&lt;p&gt;For example, if the host is &lt;code&gt;MYAWESOMEDOMAIN.COM&lt;/code&gt;, then the URL is &lt;code&gt;https://MYAWESOMEDOMAIN.COM/wp-json/wp/v2/media&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;See below to &lt;strong&gt;test specific user access to the WP REST API&lt;/strong&gt;.  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open a terminal and type the following command. Replace &lt;code&gt;USERNAME:PASSWORD&lt;/code&gt; with your username and application password.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0wKDwLf2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dennislwm.netlify.app/images/deploy-a-wordpress-pipeline-to-minimize-and-upload-images/curl-test.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0wKDwLf2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dennislwm.netlify.app/images/deploy-a-wordpress-pipeline-to-minimize-and-upload-images/curl-test.png" alt="curl command to test specific user access" width="880" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If successfully connected, you should get a &lt;code&gt;JSON&lt;/code&gt; response. For example:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   {
      "id":5831,
      "date":"2021-10-15T22:41:47",
      "date_gmt":"2021-10-15T14:41:47",
      "guid":{
         "rendered":"https:\/\/MYAWESOMEDOMAIN.COM\/wp-content\/uploads\/2021\/10\/26-September-2021-Infant-Baptism.jpg"
      },
      "modified":"2021-10-15T22:41:47",
      "modified_gmt":"2021-10-15T14:41:47",
      "slug":"26-september-2021-infant-baptism-2",
      "status":"inherit",
      "type":"attachment",
      "link":"https:\/\/MYAWESOMEDOMAIN.COM\/26-september-2021-infant-baptism\/26-september-2021-infant-baptism-2\/",
      "title":{
         "rendered":"26 September 2021 &amp;amp;#8211; Infant Baptism"
      },
      "author":3,
      "comment_status":"closed",
      "ping_status":"closed",
      "template":"",
      "meta":{
         "ngg_post_thumbnail":0
      },
      "description":{
         "rendered":"&amp;lt;p class=\"attachment\"&amp;gt;&amp;lt;a href='https:\/\/MYAWESOMEDOMAIN.COM\/wp-content\/uploads\/2021\/10\/26-September-2021-Infant-Baptism.jpg'&amp;gt;&amp;lt;img width=\"300\" height=\"200\" src=\"https:\/\/MYAWESOMEDOMAIN.COM\/wp-content\/uploads\/2021\/10\/26-September-2021-Infant-Baptism-300x200.jpg\" class=\"attachment-medium size-medium\" alt=\"\" srcset=\"https:\/\/MYAWESOMEDOMAIN.COM\/wp-content\/uploads\/2021\/10\/26-September-2021-Infant-Baptism-300x200.jpg 300w, https:\/\/MYAWESOMEDOMAIN.COM\/wp-content\/uploads\/2021\/10\/26-September-2021-Infant-Baptism-1024x683.jpg 1024w, https:\/\/MYAWESOMEDOMAIN.COM\/wp-content\/uploads\/2021\/10\/26-September-2021-Infant-Baptism-768x512.jpg 768w, https:\/\/MYAWESOMEDOMAIN.COM\/wp-content\/uploads\/2021\/10\/26-September-2021-Infant-Baptism.jpg 1405w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/&amp;gt;&amp;lt;\/a&amp;gt;&amp;lt;\/p&amp;gt;\n"
      },
      "caption":{
         "rendered":""
      },
      "alt_text":"",
      "media_type":"image",
      "mime_type":"image\/jpeg",
      "media_details":{
         "width":1405,
         "height":937,
         "file":"2021\/10\/26-September-2021-Infant-Baptism.jpg",
         "sizes":{
            "medium":{
               "file":"26-September-2021-Infant-Baptism-300x200.jpg",
               "width":300,
               "height":200,
               "mime_type":"image\/jpeg",

      }
   }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial, you used a Shell function to minimize multiple images and upload them to your shared &lt;strong&gt;WordPress&lt;/strong&gt; account. You then used the &lt;code&gt;id&lt;/code&gt; of each image to perform further processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What To Do Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Implement Compression - This is to minimize each image before uploading, e.g. package the Node.js script that uses &lt;code&gt;gulp-imagemin&lt;/code&gt; into an executable.&lt;/li&gt;
&lt;li&gt;Implement Assertions - This is to check for valid extensions of each image file to ensure that the &lt;code&gt;curl&lt;/code&gt; command doesn’t fail, e.g. &lt;code&gt;jpg&lt;/code&gt; is not a valid extension.&lt;/li&gt;
&lt;li&gt;Implement Pipeline - This is to trigger the Shell function from a pipeline, e.g. execute &lt;code&gt;wp-upload&lt;/code&gt; on a &lt;code&gt;git push&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add Processing - This is to process each image file uploaded given their unique IDs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Error Access to REST API requests is restricted by iThemes Security settings.
&lt;/h3&gt;

&lt;p&gt;You need to install &lt;strong&gt;Application Passwords&lt;/strong&gt; plugin, and allow a specific user to access WP REST API.&lt;/p&gt;

&lt;p&gt;Ensure that your username and application password is correct, when passed as a &lt;code&gt;base64&lt;/code&gt; string to WP REST API &lt;strong&gt;Basic Authentication&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Installing Minikube on Ubuntu with Windows Subsystem Linux Backend</title>
      <dc:creator>dennislwm</dc:creator>
      <pubDate>Tue, 15 Jun 2021 04:00:06 +0000</pubDate>
      <link>https://dev.to/dennislwm/installing-minikube-on-ubuntu-with-windows-subsystem-linux-backend-36g5</link>
      <guid>https://dev.to/dennislwm/installing-minikube-on-ubuntu-with-windows-subsystem-linux-backend-36g5</guid>
      <description>&lt;p&gt;&lt;strong&gt;minikube&lt;/strong&gt; is local Kubernetes, focusing on making it easy to learn and develop for Kubernetes.&lt;/p&gt;

&lt;p&gt;Why write an article on installing minikube on &lt;strong&gt;Ubuntu&lt;/strong&gt; with &lt;strong&gt;Windows Subsystem Linux&lt;/strong&gt; (WSL) 2 backend? There are two reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;the official minikube site has documentation for installing on Windows, Linux, and macOS, but not on Ubuntu with WSL 2 backend.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;compared to the macOS or Linux, the installation process on Ubuntu with WSL 2 backend is not a trivial task.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Preparing your Windows 10 machine
&lt;/h2&gt;

&lt;p&gt;What you’ll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2 CPUs or more&lt;/li&gt;
&lt;li&gt;2Gb of free memory&lt;/li&gt;
&lt;li&gt;20Gb of free disk space&lt;/li&gt;
&lt;li&gt;Internet connection&lt;/li&gt;
&lt;li&gt;Windows Subsystem Linux [“WSL”] 2&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;You can install WSL 2 by following this article &lt;a href="https://docs.microsoft.com/en-us/windows/wsl/install-win10"&gt;Install WSL on Windows 10&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Ubuntu 18.04+&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;You can install Ubuntu on WSL 2 by following this article &lt;a href="https://ubuntu.com/blog/ubuntu-on-wsl-2-is-generally-available"&gt;Ubuntu on WSL 2 Is Generally Available&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Container or virtual machine manager&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;All you need is Docker (or similarly compatible) container or a Virtual Machine environment, and Kubernetes is a single command away: &lt;code&gt;minikube start&lt;/code&gt;. You can install Docker Desktop for Windows by following this article &lt;a href="https://docs.docker.com/docker-for-windows/wsl"&gt;Docker Desktop WSL 2 backend&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Installing minikube on Ubuntu 18.04 with Windows Subsystem Linux 2 backend
&lt;/h2&gt;

&lt;p&gt;Download and install the latest minikube package for Ubuntu.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube_latest_amd64.deb
sudo dpkg -i minikube_latest_amd64.deb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuring VM driver
&lt;/h2&gt;

&lt;p&gt;As we’re on a virtual machine, we should set VM driver to none, as we cannot virtualize on virtualization.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo minikube config set vm-driver none
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the following output, which you can ignore as we don’t have minikube running yet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;These changes will take effect upon a minikube delete and then a minikube start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Changing permissions
&lt;/h2&gt;

&lt;p&gt;First, change permissions for your &lt;code&gt;$USER&lt;/code&gt; to the &lt;code&gt;.minikube&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo chown -R $USER $HOME/.minikube
sudo chmod -R u+wrx $HOME/.minikube
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installing package dependencies
&lt;/h2&gt;

&lt;p&gt;Ensure that the following packages are installed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install -y conntrack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deleting previous minikube profiles
&lt;/h2&gt;

&lt;p&gt;Check if an existing profile exists using &lt;code&gt;sudo minikube profile list&lt;/code&gt;. Delete all existing profiles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo minikube delete --purge=true --all=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Starting minikube
&lt;/h2&gt;

&lt;p&gt;Finally, start &lt;code&gt;minikube&lt;/code&gt; using the following command &lt;strong&gt;without&lt;/strong&gt; &lt;code&gt;sudo&lt;/code&gt; privileges:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;minikube start --driver=docker --delete-on-failure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Warning: The option &lt;code&gt;--driver=none&lt;/code&gt; should not be used in Windows.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A successful output should have the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;😄 minikube v1.20.0 on Ubuntu 18.04
✨ Using the docker driver based on user configuration
👍 Starting control plane node minikube in cluster minikube
🚜 Pulling base image ...
💾 Downloading Kubernetes v1.20.2 preload ...
    &amp;gt; preloaded-images-k8s-v10-v1...: 491.71 MiB / 491.71 MiB 100.00% 7.71 MiB
    &amp;gt; gcr.io/k8s-minikube/kicbase...: 358.09 MiB / 358.10 MiB 100.00% 5.30 MiB
    &amp;gt; gcr.io/k8s-minikube/kicbase...: 358.10 MiB / 358.10 MiB 100.00% 5.90 MiB
🔥 Creating docker container (CPUs=2, Memory=2200MB) ...
🐳 Preparing Kubernetes v1.20.2 on Docker 20.10.6 ...
    ▪ Generating certificates and keys ...
    ▪ Booting up control plane ...
    ▪ Configuring RBAC rules ...
🔎 Verifying Kubernetes components...
    ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
🌟 Enabled addons: storage-provisioner, default-storageclass
🏄 Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;If you get the following error on &lt;code&gt;sudo minikube start&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Exiting due to GUEST_MISSING_CONNTRACK: Sorry, Kubernetes 1.20.2 requires conntrack to be installed in root's path
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following command should resolve the above issue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install -y conntrack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;If you get the following error on &lt;code&gt;sudo minikube start --driver=docker&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Exiting due to DRV_AS_ROOT: The "docker" driver should not be used with root privileges.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should perform &lt;code&gt;minikube start --driver=docker&lt;/code&gt; without &lt;code&gt;sudo&lt;/code&gt; privilege.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If you get the following error on &lt;code&gt;minikube start --driver=docker&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Exiting due to HOST_HOME_PERMISSION: Failed to save config: open /home/dennislwm/.minikube/profiles/minikube/config.json: permission denied
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following command should resolve the above issue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo chown -R $USER $HOME/.minikube; chmod -R u+wrx $HOME/.minikube
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;If you get the following error on &lt;code&gt;sudo minikube start --driver=docker&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Exiting due to GUEST_DRIVER_MISMATCH: The existing "minikube" cluster was created using the "none" driver, which is incompatible with requested "docker" driver.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check if an existing profile exists using &lt;code&gt;sudo minikube profile list&lt;/code&gt;. The following command should resolve the above issue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo minikube delete --purge=true --all=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Was this article useful? Help us to improve!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;With your feedback, we can improve the newsletter. Click on a link to vote: 🗳️&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://feedletter.co/feedback/give/1/98147649-0679-4569-9815-2460979f69be"&gt;👍 Thanks - this issue helped me.&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://feedletter.co/feedback/give/2/98147649-0679-4569-9815-2460979f69be"&gt;😐 Meh - was ok.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://feedletter.co/feedback/give/3/98147649-0679-4569-9815-2460979f69be"&gt;👎 Not interesting to me.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/dennislwm"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HBeonlhp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://img.shields.io/badge/buy%2520me%2520a%2520coffee-donate-yellow.svg" alt="Buy Me A Coffee donate button"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>minikube</category>
      <category>kubernetes</category>
      <category>wsl</category>
    </item>
    <item>
      <title>Makerwork 008 has an interview with Pascal of Yummyplan a meal planner</title>
      <dc:creator>dennislwm</dc:creator>
      <pubDate>Wed, 07 Apr 2021 04:00:06 +0000</pubDate>
      <link>https://dev.to/dennislwm/makerwork-008-3ip3</link>
      <guid>https://dev.to/dennislwm/makerwork-008-3ip3</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ILaWW7UZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xvvja55lrt404eavikez.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ILaWW7UZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xvvja55lrt404eavikez.png" alt="Makerwork"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Hey Makers! 👋
&lt;/h2&gt;

&lt;p&gt;Welcome to &lt;strong&gt;Digest 008&lt;/strong&gt; of the newsletter. Our featured maker, Pascal, is the author of a web app to create a meal plan for an entire week. 🏆&lt;/p&gt;

&lt;p&gt;In a 2018 survey by V. Botev, almost 76% of the subjects stated that they do cook while a small subset of them said they were good at it (26%). Only 13% of the subjects said they know exactly what to cook every time, while 53% had trouble deciding on the meal.&lt;/p&gt;

&lt;p&gt;People like cooking at home their own meals however they do have a problem getting from the step where they decide to cook to the step where they actually start cooking. In other words, people had little free time during their busy day for planning their meals.&lt;/p&gt;

&lt;p&gt;Fortunately, this problem can be easily solved by using a meal planning app, &lt;strong&gt;Yummyplan&lt;/strong&gt; , that can bring down the process of meal planning from 30 minutes to around 5 minutes. ⭐️&lt;/p&gt;

&lt;h2&gt;
  
  
  What I’ve Been Reading 📖
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;⭐️ &lt;a href="https://dev.to/thormeier/let-s-build-an-actual-working-guitar-with-javascript-bfb"&gt;Let’s build an actual working Guitar🎸 with JavaScript 💻🤘&lt;/a&gt;: &lt;em&gt;Let’s build a guitar! Well, ok, not a physical guitar, but the next best thing: A digital one!&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://anthonynsimon.com/blog/tools-of-the-trade"&gt;Why I Run Django on Kubernetes as a One-Man SaaS&lt;/a&gt;: &lt;em&gt;When I first published my post on my tech stack as a one-man SaaS I got wildly different reactions.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://notfunatparties.substack.com/p/inside-a-viral-website"&gt;Inside a viral website - Not Fun at Parties&lt;/a&gt;: &lt;em&gt;As anyone reading this knows, the Ever Given was stuck in the Suez Canal for just over 6 days. This is an account of running istheshipstillstuck.com.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.indiehackers.com/post/the-story-of-a-unicorn-solo-founder-making-500-000-arr-4c3070f0f0"&gt;The Story of a Unicorn Solo Founder Making $500,000 ARR 🚀&lt;/a&gt;: &lt;em&gt;Do what you love, build projects and features people want, and the success will come. I bet you heard that many times.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.remidiy.com/blog/first-launch"&gt;How I Launched ValueDensity in One Week&lt;/a&gt;: *In my previous article, I’ve explained why I quit my lucrative data scientist job, 18 months back, to become an indiehacker. *&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.swyx.io/part-time-creator-manifesto"&gt;The Part Time Creator Manifesto&lt;/a&gt;: &lt;em&gt;Something unexpected happened when my side project crossed $100k in sales: I didn’t want to quit my job.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Maker Focus 🏆
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Featuring one or more makers per digest. Want to be featured as a maker? Fill in this &lt;a href="https://yourls.fxgit.work/010jotsh"&gt;form&lt;/a&gt;. ✍️&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Pascal Thormeier
&lt;/h3&gt;

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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;a href="https://twitter.com/pthormeier"&gt;Twitter&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://linkedin.com/in/thormeier"&gt;LinkedIn&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://github.com/thormeier"&gt;GitHub&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://dev.to/thormeier"&gt;Dev.to&lt;/a&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Pascal is skilled in problem solving, asking why and how, ability to learn and to adapt quickly. 📦&lt;/p&gt;

&lt;p&gt;He is currently re-learning electronics and soldering. 🤖&lt;/p&gt;




&lt;h2&gt;
  
  
  Product Focus 📦
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://yummyplan.app"&gt;Yummyplan&lt;/a&gt; ⭐️
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;An app to create a meal plan for an entire week.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://yummyplan.app"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jHP4D1Xn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rwjjypg8kjh061zg3j4c.png" alt="*Yummyplan*"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Stars&lt;/th&gt;
&lt;th&gt;Forks&lt;/th&gt;
&lt;th&gt;Issues&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TypeScript, Vue&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Last commit&lt;/th&gt;
&lt;th&gt;First commit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mar 28, 2021&lt;/td&gt;
&lt;td&gt;Mar 07, 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Yummyplan&lt;/strong&gt; is an app that helps you plan your meals for an entire week! Add new meals and ingredients, tag them, plan them, download your week plan and generate a grocery list, all in one.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://logchimp.codecarrot.net"&gt;LogChimp&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Build better products with customer feedback.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://logchimp.codecarrot.net"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aR0jjx9i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l41ddxtjqjz62guzg2fp.png" alt="*LogChimp*"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Stars&lt;/th&gt;
&lt;th&gt;Forks&lt;/th&gt;
&lt;th&gt;Issues&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JavaScript, Html&lt;/td&gt;
&lt;td&gt;143&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Last commit&lt;/th&gt;
&lt;th&gt;First commit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Apr 03, 2021&lt;/td&gt;
&lt;td&gt;Jan 19, 2020&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;LogChimp&lt;/strong&gt; is an open source software to capture your customers feedback and inform your product decisions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Open source&lt;/li&gt;
&lt;li&gt;✅ Self-hosted&lt;/li&gt;
&lt;li&gt;✅ Secure and full control of data&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://freescout.net"&gt;FreeScout&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Free self-hosted Zendesk &amp;amp; Help Scout alternative.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://freescout.net"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y24aoXmf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rgpx81ehgcsbm07640of.png" alt="*FreeScout*"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Stars&lt;/th&gt;
&lt;th&gt;Forks&lt;/th&gt;
&lt;th&gt;Issues&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Php, Blade&lt;/td&gt;
&lt;td&gt;1k&lt;/td&gt;
&lt;td&gt;180&lt;/td&gt;
&lt;td&gt;132&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Last commit&lt;/th&gt;
&lt;th&gt;First commit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Apr 02, 2021&lt;/td&gt;
&lt;td&gt;Jun 22, 2018&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;FreeScout&lt;/strong&gt; is the super lightweight free open source help desk and shared inbox written in PHP (Laravel framework). It is a self hosted clone of HelpScout.&lt;/p&gt;

&lt;p&gt;Now you can enjoy free Zendesk &amp;amp; HelpScout without giving up privacy or locking you into a service you don’t control.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tweet Focus
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://twitter.com/joulee/status/1377645723118792707"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EufG7HzB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/avf5a6lmqmweh3qfawmw.png" alt="*tweet*"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Education 📚 &amp;amp; Resources 🧩
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://basecamp.com/gettingreal"&gt;Getting Real&lt;/a&gt;: &lt;em&gt;The smarter, faster, easier way to build a successful web application.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://basecamp.com/shapeup"&gt;Shape Up&lt;/a&gt;: &lt;em&gt;Stop running in circles and ship work that matters.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://yangshun.github.io/tech-interview-handbook"&gt;Tech Interview Handbook&lt;/a&gt;: &lt;em&gt;Carefully curated content to help you ace your next technical interview.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://usedevbook.com"&gt;Devbook&lt;/a&gt;: &lt;em&gt;Devbook is a new kind of search engine made just for developers.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.writenext.io"&gt;WriteNext&lt;/a&gt;: &lt;em&gt;The writing application that boosts your writing.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://quizlet.com"&gt;Quizlet&lt;/a&gt;: &lt;em&gt;Learning tools &amp;amp; flashcards, for free.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  An Interview with Pascal of Yummyplan
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Hey Pascal! Let’s start out with your background. 📝&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I’m a Software engineer from Switzerland and I love solving problems, learning new things and sharing my knowledge.&lt;/p&gt;

&lt;p&gt;I originally got into coding while working with the RPGMaker2000 back in high school. The RPGMaker2000 is a tool to create your own 2D games in the style of Final Fantasy 2 and the like.&lt;/p&gt;

&lt;p&gt;It had a scripting system with if/else blocks, loops, integers and boolean variables etc. That was my first point of contact with code.&lt;/p&gt;

&lt;p&gt;At some point I wanted my own website to show off my projects, so I learned HTML, CSS and PHP. Along came professional education and some 10 years working in the field.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;What is the purpose of your blog, and what resources do you use to get your ideas, if any? 🧩&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sharing my knowledge about all things software development and web. I often write about tools, algorithms and cool stuff I made.&lt;/p&gt;

&lt;p&gt;The internet, mostly search engines. Often I come up with ideas myself, but search engines help a lot when I’m stuck.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;What is an opinion you have that most people don’t agree with? ✒️&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Luck plays a significant role in being successful. You need to meet the right people (you often don’t even know these people exist in the first place), publish your stuff everywhere, solve a problem for you have and see if any other people have the same problem and like your solution.&lt;/p&gt;

&lt;p&gt;Success, as I’ve learned, is basically trying over and over and, at some point, a happy coincidence helping you gain traction.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Why was Yummyplan started and is there a roadmap? 🎯&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I originally created Yummyplan to digitize an analog solution (with paper and sticky notes) we used at home. I wanted this thing to be as useful as possible and to take away a maximum of the boring work, like looking for that one sticky, random meals if we’re out of ideas or writing the grocery list.&lt;/p&gt;

&lt;p&gt;The frontend application is currently published under the MIT license, so it’s open source. Since Yummyplan is more of a hobby project, there is a roadmap, but not really a detailed one.&lt;/p&gt;

&lt;p&gt;Next on my list are further bug fixes and a backend to enable cross-device sharing - I got a lot of feedback that this was a highly desired feature.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;What is the most challenging problem that’s been solved in Yummyplan so far? 🚧&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Definitely the grocery list! There’s multiple steps to this behemoth of a feature.&lt;/p&gt;

&lt;p&gt;First, it needs to collect all the ingredients of all selected menus. Those need to be added up (example: if two menus each use 1 liter of milk, the grocery list should show “2 liters of milk”) and are then sorted into categories to make the grocercy trip a lot simpler.&lt;/p&gt;

&lt;p&gt;Then there’s the “tick off” feature, a separate list of items don’t need to be bought. The PDF-download was its very own odyssey and I’m still tweaking it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;What is one feature, which you’re most proud of, that differentiates Yummyplan from other products? 🦄&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The randomize button and the grocery list are among my favorites. They solve problems we had in real life.&lt;/p&gt;

&lt;p&gt;When we were planing our meals with stickies on a piece of paper, we often ran out of ideas. Also, we often forgot grocery items because we didn’t think of them.&lt;/p&gt;

&lt;p&gt;Those two features alone brought the process of meal planning from 30 minutes down to around 5 minutes and we never forgot to buy anything crucial ever since.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;What is one product that you can’t live without that you think others should know about? 💡&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Vivaldi, a browser by the ex core team of Opera. It lives up to the spirit and is an absolute bliss for power users!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;If I gave you $1 million to invest in one thing right now, where would you put it? 🚀&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Non-profits that help the less-privileged. Some of them, like cafes that offer safe spaces or sports clubs, really struggle in the current situation and could use some financial support.&lt;/p&gt;

&lt;p&gt;I’d probably also invest in some startups that work on clean energy or making education more accessibly. ☀️&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Thank you Pascal.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Was Makerwork useful? Help us to improve!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;With your feedback, we can improve the newsletter. Click on a link to vote: 🗳️&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://feedletter.co/feedback/give/1/292643327588696579"&gt;👍 Thanks - this issue helped me.&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://feedletter.co/feedback/give/2/292643327588696579"&gt;😐 Meh - was ok.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://feedletter.co/feedback/give/3/292643327588696579"&gt;👎 Not interesting to me.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/dennislwm"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HBeonlhp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://img.shields.io/badge/buy%2520me%2520a%2520coffee-donate-yellow.svg" alt="Buy Me A Coffee donate button"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Like what you saw here? Why not share it?&lt;/p&gt;

</description>
      <category>helpdesk</category>
      <category>vue</category>
      <category>mealplanner</category>
      <category>makerwork</category>
    </item>
    <item>
      <title>Makerwork 007 has an interview with Antonio of theLIFEBOARD a life habit planner</title>
      <dc:creator>dennislwm</dc:creator>
      <pubDate>Wed, 10 Mar 2021 04:00:06 +0000</pubDate>
      <link>https://dev.to/dennislwm/makerwork-007-has-an-interview-with-antonio-of-thelifeboard-a-life-habit-planner-4f5g</link>
      <guid>https://dev.to/dennislwm/makerwork-007-has-an-interview-with-antonio-of-thelifeboard-a-life-habit-planner-4f5g</guid>
      <description>&lt;p&gt;Hey Makers! 👋 &lt;/p&gt;

&lt;p&gt;Welcome to &lt;strong&gt;Digest 007&lt;/strong&gt; of the newsletter. Our featured maker, Antonio, started with an idea to build a new year resolutions web app to scratch his own itch. 🏆 &lt;/p&gt;

&lt;p&gt;For many of us, nine months spent quarantining at home has completely erased the elaborate routines and habits we had carefully constructed in the pre pandemic. So it’s unsurprising that as we turn to 2021, many people are seeking ways to develop new habits and bring some structure and routine back into their largely confined lives.&lt;/p&gt;

&lt;p&gt;A new survey from CIT Bank found that 43 percent of Americans are setting new year resolutions for 2021, compared with 35 percent who did the same for 2020. Resolutions focused on habits such as exercise and self-care are especially popular.&lt;/p&gt;

&lt;p&gt;While we all start a new year with good intentions that we’ll reach our goals, however, few of us stick to these resolutions past January. A study by the University of Scranton found that just 40 percent of resolution makers are still keeping their resolutions six months in.&lt;/p&gt;

&lt;p&gt;That’s where habit tracking apps want to help you. Unlike logging apps, habit formation apps are a slightly different breed. They’re aspirational. Habit apps are less about distilling your life into a series of data points and more about becoming your ideal self.&lt;/p&gt;

&lt;p&gt;A habit tracker can make goals feel more attainable, boosts your confidence, keeps you focused and helps you stay motivated. theLifeBoard app does all these and more by following a simple methodology: plan, execute, analyze, improve, repeat.&lt;/p&gt;

&lt;p&gt;After all who can give a better testimonial of &lt;strong&gt;theLifeboard&lt;/strong&gt; app than the author himself. Antonio has been following this methodology for almost four years now with great results. ⭐️&lt;/p&gt;

&lt;p&gt;If you like to know about the maker, I interviewed Antonio of &lt;strong&gt;theLifeboard&lt;/strong&gt; in this week's roundup of makers for makers. This week's issue also includes &lt;strong&gt;BeeWare&lt;/strong&gt; a python suite to write once and deploy everywhere, and an open source powerful yet simple to use screenshot software &lt;strong&gt;Flameshot&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://makerwork.substack.com"&gt;SUBSCRIBE FREE&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Makerwork newsletter includes interviews with makers featured in the email sharing their insights about open-source, their product, blog and work.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>tracker</category>
      <category>makerwork</category>
    </item>
    <item>
      <title>Makerwork 006 includes a universal tool to evaluate, discover and compare software projects on GitHub</title>
      <dc:creator>dennislwm</dc:creator>
      <pubDate>Wed, 03 Mar 2021 04:00:06 +0000</pubDate>
      <link>https://dev.to/dennislwm/makerwork-006-includes-a-universal-tool-to-evaluate-discover-and-compare-software-projects-on-github-2dd8</link>
      <guid>https://dev.to/dennislwm/makerwork-006-includes-a-universal-tool-to-evaluate-discover-and-compare-software-projects-on-github-2dd8</guid>
      <description>&lt;p&gt;Hey Makers! 👋 &lt;/p&gt;

&lt;p&gt;Welcome to &lt;strong&gt;Digest 006&lt;/strong&gt; of the newsletter. Our featured maker, Alexey, is the author of a universal tool to evaluate and discover alternatives and compare software projects on GitHub. 🏆 &lt;/p&gt;

&lt;p&gt;By the end of 2017, over 25 million project repositories were hosted on GitHub and we all know that the number is counting. With so many options out there, it’s hard to decide which one is best for you.&lt;/p&gt;

&lt;p&gt;Given there are competing tools, it is important not merely to find the appropriate software, but have a metric for judging its usefulness. Indeed, GitHub statistics (counts of stars, watches and forks) were highly correlated with each other, but were distinct from other metrics.&lt;/p&gt;

&lt;p&gt;Metrics such as downloads, usage, releases etc may necessarily indicate the software usefulness. Alexey has never seen a tool that could compare GitHub repositories side by side with a broad range of data and statistics.&lt;/p&gt;

&lt;p&gt;We suggest that &lt;strong&gt;Moiva&lt;/strong&gt; should be used as an objective addition in measuring the utility of software, while Alexey’s goal is to make it really useful for as many developers as possible, make it popular, and present it in a nice and elegant way. ⭐️&lt;/p&gt;

&lt;p&gt;If you like to know about the maker, I interviewed Alexey of &lt;strong&gt;Moiva&lt;/strong&gt; in this week's roundup of makers for makers. This week's issue also includes an an open source ticket management system &lt;strong&gt;Peppermint&lt;/strong&gt; and an open source alternative to Clubhouse, Twitter Spaces and similar audio spaces &lt;strong&gt;Jam&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://makerwork.substack.com"&gt;SUBSCRIBE FREE&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Makerwork newsletter includes interviews with makers featured in the email sharing their insights about open-source, their product, blog and work.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>github</category>
      <category>vue</category>
      <category>javascript</category>
      <category>makerwork</category>
    </item>
    <item>
      <title>Makerwork 005 includes an open-source text-based programming language for music composition</title>
      <dc:creator>dennislwm</dc:creator>
      <pubDate>Mon, 22 Feb 2021 04:00:06 +0000</pubDate>
      <link>https://dev.to/dennislwm/makerwork-005-4686</link>
      <guid>https://dev.to/dennislwm/makerwork-005-4686</guid>
      <description>&lt;p&gt;Hey Makers! 👋&lt;/p&gt;

&lt;p&gt;Welcome to &lt;strong&gt;Digest 005&lt;/strong&gt; of the newsletter. Our featured maker, Dave, is the author of a text-based programming language for music composition. 🏆&lt;/p&gt;

&lt;p&gt;As a programmer and as a composer, Dave felt that there are a couple of fundamental problems with GUI music notation software: it’s distracting and it’s limiting.&lt;/p&gt;

&lt;p&gt;While some people may be put off by a wall of textual code, text-based languages are both very compact and open. To help beginners overcome this, Dave has put a lot of thought into making the syntax as intuitive and beginner-friendly as possible.&lt;/p&gt;

&lt;p&gt;Dave is envisioning a world where both programmers and non-programmers alike can create all sorts of music, from classical to chiptune to experimental soundscapes, using only a text editor and the &lt;strong&gt;Alda&lt;/strong&gt; executable. ⭐️&lt;/p&gt;

&lt;p&gt;If you like to know about the maker, I interviewed Dave of &lt;strong&gt;Alda&lt;/strong&gt; in this week's roundup of makers for makers. This week's issue also includes an open source and privacy-focused productivity suite &lt;strong&gt;Bloom&lt;/strong&gt; and a web-based multiuser time-tracking application &lt;strong&gt;Kimai&lt;/strong&gt; free for everyone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://makerwork.substack.com"&gt;SUBSCRIBE FREE&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Makerwork newsletter includes interviews with makers featured in the email sharing their insights about open-source, their product, blog and work.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>clojure</category>
      <category>music</category>
      <category>rust</category>
      <category>makerwork</category>
    </item>
    <item>
      <title>Makerwork 004</title>
      <dc:creator>dennislwm</dc:creator>
      <pubDate>Mon, 15 Feb 2021 04:00:06 +0000</pubDate>
      <link>https://dev.to/dennislwm/makerwork-004-3nfe</link>
      <guid>https://dev.to/dennislwm/makerwork-004-3nfe</guid>
      <description>&lt;p&gt;Hey Makers! 👋 &lt;/p&gt;

&lt;p&gt;Kevin, our featured maker, created &lt;strong&gt;InvoicePlane&lt;/strong&gt; and grew its users to thousands before feeling burned out and passing it to another maintainer after 4 years of work. He is now maintaining a bookmarking app that he created to scratch his own itch. 🏆 &lt;/p&gt;

&lt;p&gt;Bookmarks are very popular among users and are used slightly more than other navigation strategies, such as querying a search engine. Over 90% of users have a bookmark archive according to research from the University of Toronto.&lt;/p&gt;

&lt;p&gt;As the Internet becomes the dominant global information resource, users will need bookmark management systems that scale-up to manage their growing archives. Systems should minimize the effort needed to organize bookmarks, ideally an automated sorting system.&lt;/p&gt;

&lt;p&gt;Poor visualization hampers users with large archives, while limited information in the top level listing of bookmarks makes it difficult to find bookmarks. With skills in web and backend development, Kevin created &lt;strong&gt;LinkAce&lt;/strong&gt; with a focus on user management and handling. ⭐️&lt;/p&gt;

&lt;p&gt;If you like to know about the maker, I interviewed Kevin of &lt;strong&gt;LinkAce&lt;/strong&gt; in a weekly roundup of makers for makers. &lt;/p&gt;

&lt;p&gt;Makerwork 004 includes an open source userscripts support for browsers, namely &lt;strong&gt;Violentmonkey&lt;/strong&gt;, and &lt;strong&gt;Limus&lt;/strong&gt; a self-hosted image/screenshot editor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://makerwork.substack.com"&gt;SUBSCRIBE FREE&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Makerwork newsletter includes interviews with makers featured in the email sharing their insights about open-source, their product, blog and work.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>bookmarker</category>
      <category>browser</category>
      <category>makerwork</category>
    </item>
  </channel>
</rss>
