<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
<title type="text">Digital Moleskine</title>
<generator uri="https://github.com/jekyll/jekyll">Jekyll</generator>
<link rel="self" type="application/atom+xml" href="https://kuzmi.ch/feed.xml" />
<link rel="alternate" type="text/html" href="https://kuzmi.ch" />
<updated>2025-07-18T07:57:51+00:00</updated>
<id>https://kuzmi.ch/</id>
<author>
  <name>Sergey Kuzmich</name>
  <uri>https://kuzmi.ch/</uri>
  <email>contact@kuzmi.ch</email>
</author>


<entry>
  <title type="html"><![CDATA[Cursor Way of Pagination]]></title>
  <link rel="alternate" type="text/html" href="https://kuzmi.ch/notes/cursor-based-pagination/" />
  <id>https://kuzmi.ch/notes/cursor-based-pagination</id>
  <published>2022-11-27T00:00:00+00:00</published>
  <updated>2022-11-27T00:00:00+00:00</updated>
  <author>
    <name>Sergey Kuzmich</name>
    <uri>https://kuzmi.ch</uri>
    <email>contact@kuzmi.ch</email>
  </author>
  <content type="html">
    &lt;p&gt;Many articles and notes have been written about the disadvantages of “regular” pages when browsing lists of content. The major disadvantages are the speed of SQL &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OFFSET&lt;/code&gt; and duplicates of “edge” elements.&lt;/p&gt;

&lt;p&gt;The next generation of dynamically changing content (blog feeds, tweets, Instagram posts) requires advanced content navigation approaches. One of them is cursor-based pagination.&lt;/p&gt;

&lt;p&gt;The idea of cursor-based pagination is moving between “next” and “previous” pages quickly, without duplicates or missing content. Usually users don’t have “pages” navigation—it looks like infinite scroll on the feed page. Examples of this type of navigation are the infinite feeds of Twitter, Instagram, Facebook, etc.&lt;/p&gt;

&lt;p&gt;Developers who integrate with this type of pagination also don’t have “pages” in the API but have a “cursor” to the next or previous page. It looks like:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/60ba05b97448c9595434cb35ea4afb84.js&quot;&gt; &lt;/script&gt;

&lt;h2 id=&quot;implementation&quot;&gt;Implementation&lt;/h2&gt;

&lt;p&gt;The minimal model of any content is:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;PRIMARY KEY&lt;/strong&gt; – unique item identifier, could be integer, uuid, or any other type;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;DATA&lt;/strong&gt; – a field, or number of fields of “human” content;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;ORDER&lt;/strong&gt; – a field, or number of fields to determine the order content should be displayed in;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Modern platforms use algorithms to determine the order of the content for each user; this approach is out of scope for this article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since the &lt;strong&gt;ORDER&lt;/strong&gt; field often is not unique, we can’t use it exclusively. We need something more reliable than that.&lt;/p&gt;

&lt;p&gt;So let’s build the pagination of news feed. We’ll use item created date as an &lt;strong&gt;ORDER&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The table &lt;strong&gt;news&lt;/strong&gt; will look as follows.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; uid | title           | content                       | datetime
--------------------------------------------------------------------
 ... | World War Z     | Lorem ipsum dolor sit ...     | 1638004125
 ... | Etiam Molestie  | Maecenas vulputate mi ...     | 1624774080
 ... | Nam eu Lobortis | Mauris facilisis sagittis ... | 1614406080
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Since &lt;em&gt;datetime&lt;/em&gt; is not unique, but &lt;em&gt;ID&lt;/em&gt; is, we need to create a single directional list of items using it.&lt;/p&gt;

&lt;p&gt;The database index will help to chain items by those fields.
&lt;script src=&quot;https://gist.github.com/5b4936232c1d8cae5f3c39f57376df2a.js&quot;&gt; &lt;/script&gt;&lt;/p&gt;

&lt;p&gt;Now query to get the next page will be:
&lt;script src=&quot;https://gist.github.com/22652f6ed9e4b05b009bc63d35d101a2.js&quot;&gt; &lt;/script&gt;&lt;/p&gt;

&lt;p&gt;Links to previous and next pages could be provided by the API as part of the response, but the application using the API could also build them on its own according to the latest response.&lt;/p&gt;

&lt;p&gt;Getting the previous page is a little bit different:
&lt;script src=&quot;https://gist.github.com/7f075577b3bb06524f7bdcc5409771b0.js&quot;&gt; &lt;/script&gt;&lt;/p&gt;

&lt;p&gt;So we change the order but then reverse the items before sending them as a response.&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;https://kuzmi.ch/notes/cursor-based-pagination/&quot;&gt;Cursor Way of Pagination&lt;/a&gt; was originally published by Sergey Kuzmich at &lt;a href=&quot;https://kuzmi.ch&quot;&gt;Digital Moleskine&lt;/a&gt; on November 27, 2022.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[WordPress Configuration Cheatsheet]]></title>
  <link rel="alternate" type="text/html" href="https://kuzmi.ch/notes/wordpress-configuration-cheat-sheet/" />
  <id>https://kuzmi.ch/notes/wordpress-configuration-cheat-sheet</id>
  <published>2021-04-26T00:00:00+00:00</published>
  <updated>2021-04-26T00:00:00+00:00</updated>
  <author>
    <name>Sergey Kuzmich</name>
    <uri>https://kuzmi.ch</uri>
    <email>contact@kuzmi.ch</email>
  </author>
  <content type="html">
    &lt;blockquote&gt;
  &lt;p&gt;Original post was located at https://blog.ripstech.com/2018/wordpress-configuration-cheat-sheet/.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;WordPress is the most frequently installed web application in the world. The system is operated not only by experienced developers but also by beginners. This post summarizes what to look out for when configuring your WordPress installation’s security.&lt;/p&gt;

&lt;h2 id=&quot;1-disable-debugging&quot;&gt;1. Disable Debugging&lt;/h2&gt;

&lt;p&gt;The debug functionality should not be active in a production environment, as it might provide useful information to potential attackers.&lt;/p&gt;

&lt;p&gt;The debug information is very helpful during the development of a WordPress application. However, it is important to make sure that the parameter is set back to false before loading the system onto a remote server. Errors should be logged, but they must not be visible to unauthorized users.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/feb85d7361bd071b5161f21c0094340c.js&quot;&gt; &lt;/script&gt;

&lt;h2 id=&quot;2-use-strong-database-credentials&quot;&gt;2. Use Strong Database Credentials&lt;/h2&gt;

&lt;p&gt;Each WordPress installation should have its own database with a dedicated user, and a secure and unique password.&lt;/p&gt;

&lt;p&gt;When installing WordPress, a table prefix can be specified. This can allow you to run several installations using one database. However, sharing one database between different installations is dangerous because if one instance gets hijacked, the other installations will also be at risk. In addition, you should avoid using the database root account, as it has full access to all databases on the server.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/b0ed374f4237b56419ab0c00c1b7c2ac.js&quot;&gt; &lt;/script&gt;

&lt;h2 id=&quot;3-use-unique-keys-and-salts&quot;&gt;3. Use Unique Keys and Salts&lt;/h2&gt;

&lt;p&gt;The salts and keys must be unique for each WordPress installation, as this is the only way to ensure secure user management.&lt;/p&gt;

&lt;p&gt;The salts and keys are important for different functionalities in a WordPress application. Among other things for a secure login and a session management. The values are automatically chosen randomly during the installation. However, a WordPress installation might be duplicated. On deployment, it is advised to generate new values for these constants. These values can be retrieved via the following API. https://api.wordpress.org/secret-key/1.1/salt/&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/134fe24ad186ad4e486210a91c35c5f1.js&quot;&gt; &lt;/script&gt;

&lt;h2 id=&quot;4-use-ssl-encryption&quot;&gt;4. Use SSL Encryption&lt;/h2&gt;

&lt;p&gt;No one should be able to read the traffic between your server and your users. Use SSL encryption and force WordPress to use only this type of connection.&lt;/p&gt;

&lt;p&gt;It is now standard practice to encrypt your traffic. Many browser manufacturers mark un-encrypted connections as dangerous. Often web hosters offer their customers simple free certificates to enable SSL encryption. Also providers like Let’s Encrypt offer free certificates.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/ea2999129d11bcbe4132a570c8e04cc4.js&quot;&gt; &lt;/script&gt;

&lt;h2 id=&quot;5-prohibit-database-repair&quot;&gt;5. Prohibit Database Repair&lt;/h2&gt;

&lt;p&gt;WordPress supports automatic database repair. This should not be possible without a backup of the database.&lt;/p&gt;

&lt;p&gt;The path &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wp-admin/maint/repair.php&lt;/code&gt;, which anyone with sufficient permissions can call, triggers this automatic process. This might be helpful for a broken or fragmented database schema. This should never be done without a proper backup of the database.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/ba2bdb93246d1d39d8f8bb2907e24dd4.js&quot;&gt; &lt;/script&gt;

&lt;h2 id=&quot;6-disallow-unfiltered-content&quot;&gt;6. Disallow Unfiltered Content&lt;/h2&gt;

&lt;p&gt;Admins and Editors are able to publish unfiltered HTML or files. If that is not absolutely necessary, activate these filters.&lt;/p&gt;

&lt;p&gt;By default, Admins and Editors are able to write unfiltered HTML in post title, post content, and comments. The filetype filter can also be deactivated. This filter should remain activated.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/3a7a0b36085a35565b1e5e9470ab628f.js&quot;&gt; &lt;/script&gt;

&lt;h2 id=&quot;7-enable-auto-security-update&quot;&gt;7. Enable Auto Security Update&lt;/h2&gt;

&lt;p&gt;Your WordPress should always be up to date so that it can resist the latest attacks. Activate the automatic security updates.&lt;/p&gt;

&lt;p&gt;Since WordPress is a wide-spread application, many attackers test for known vulnerabilities in outdated installations. Don’t forget to perform a backup before each core upgrade, so only the minor update with current security patches should be installed automatically.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/4993efef986422e485cfacd52edfa878.js&quot;&gt; &lt;/script&gt;

&lt;h2 id=&quot;8-block-external-request&quot;&gt;8. Block External Request&lt;/h2&gt;

&lt;p&gt;Access from your installation to external resources should be restricted. It is advisable to only accept trusted sources.&lt;/p&gt;

&lt;p&gt;Not only the external access to WordPress should be secured, but also the access from the system to external resources. Here, the principle of white-list instead of blacklist applies.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/e0674add87057e24ad977cd3cc9a8c9a.js&quot;&gt; &lt;/script&gt;

&lt;h2 id=&quot;9-disallow-file-modifications&quot;&gt;9. Disallow File Modifications&lt;/h2&gt;

&lt;p&gt;Prohibit the editing of code lines in plugins and themes by your admins and editors. Deactivate the file editor for all extensions.&lt;/p&gt;

&lt;p&gt;By manipulating code, a trusted extension can become a gateway for attackers.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/d0c115fb82ab831e7409c09050a139f1.js&quot;&gt; &lt;/script&gt;

&lt;h2 id=&quot;10-disallow-install-extensions&quot;&gt;10. Disallow Install Extensions&lt;/h2&gt;

&lt;p&gt;To harden your installation completely you can prevent any changes to your system by prohibiting the installation of plugins and themes.&lt;/p&gt;

&lt;p&gt;Themes and plugins can not only bring advantages for your WordPress. You must trust the creators of these extensions. Activate this barrier to prevent your admins and editors from loading untrusted extensions.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/5f99722a34613491a9162a9c8349917c.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;The cheat sheet lists the 10 most important configurations. Many of these settings are set correctly by default when installing WordPress while others can be changed to harden your security. Note that not all of the suggested settings for a secure configuration might be suitable for your application, depending on its environment and development stage. With the help of static analysis tools, these and other settings can also be checked automatically.&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;https://kuzmi.ch/notes/wordpress-configuration-cheat-sheet/&quot;&gt;WordPress Configuration Cheatsheet&lt;/a&gt; was originally published by Sergey Kuzmich at &lt;a href=&quot;https://kuzmi.ch&quot;&gt;Digital Moleskine&lt;/a&gt; on April 26, 2021.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[Git Ignore-On-Commit]]></title>
  <link rel="alternate" type="text/html" href="https://kuzmi.ch/notes/git-ignore-on-commit/" />
  <id>https://kuzmi.ch/notes/git-ignore-on-commit</id>
  <published>2019-04-07T00:00:00+00:00</published>
  <updated>2019-04-07T00:00:00+00:00</updated>
  <author>
    <name>Sergey Kuzmich</name>
    <uri>https://kuzmi.ch</uri>
    <email>contact@kuzmi.ch</email>
  </author>
  <content type="html">
    &lt;p&gt;I used SVN &amp;amp; Tortoise SVN years ago. There was one cool feature: &lt;strong&gt;ignore-on-commit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now I use Git to track projects and file changes. I’m pretty sure that Tortoise GIT has an &lt;strong&gt;ignore-on-commit&lt;/strong&gt; feature, but I prefer to use Git within the terminal. Anyway, there is a straightforward way to achieve the same behavior.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git update-index --assume-unchanged &amp;lt;filename&amp;gt;&lt;/code&gt;
  makes file changes ‘invisible’ for git tree status;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git update-index --no-assume-unchanged &amp;lt;filename&amp;gt;&lt;/code&gt;
  unhides file from being ‘invisible’ for git;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git ls-files -v | grep &apos;^h&apos; | cut -c3-&lt;/code&gt;&lt;br /&gt;
  shows all ‘invisible’ files;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is one more tip with it - use aliases. Put lines below to .gitconfig file.
&lt;script src=&quot;https://gist.github.com/8a110a1bbfcf43a985597400336c28e8.js&quot;&gt; &lt;/script&gt;&lt;/p&gt;

&lt;p&gt;Now the commands at the beginning are available the following way:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git hide &amp;lt;filename&amp;gt;&lt;/code&gt;&lt;br /&gt;
  makes file changes ‘invisible’ for git tree status;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git unhide &amp;lt;filename&amp;gt;&lt;/code&gt;
  unhides file from being ‘invisible’ for git;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git show-hidden&lt;/code&gt;&lt;br /&gt;
  shows all ‘invisible’ files;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it!&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;https://kuzmi.ch/notes/git-ignore-on-commit/&quot;&gt;Git Ignore-On-Commit&lt;/a&gt; was originally published by Sergey Kuzmich at &lt;a href=&quot;https://kuzmi.ch&quot;&gt;Digital Moleskine&lt;/a&gt; on April 07, 2019.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[Terraform Module Example]]></title>
  <link rel="alternate" type="text/html" href="https://kuzmi.ch/notes/terraform-module-example/" />
  <id>https://kuzmi.ch/notes/terraform-module-example</id>
  <published>2019-01-29T00:00:00+00:00</published>
  <updated>2019-01-29T00:00:00+00:00</updated>
  <author>
    <name>Sergey Kuzmich</name>
    <uri>https://kuzmi.ch</uri>
    <email>contact@kuzmi.ch</email>
  </author>
  <content type="html">
    &lt;blockquote&gt;
  &lt;p&gt;In addition to &lt;a href=&quot;/notes/how-to-deploy-applications-to-digitalocean-with-terraform/&quot;&gt;the previous article&lt;/a&gt; I would also like to write about &lt;a href=&quot;https://www.terraform.io/docs/modules/index.html&quot;&gt;Terraform Modules&lt;/a&gt; as a way to boilerplate common deployment tasks.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Let’s make a module to quickly deploy WordPress website with DigitalOcean provider.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;getting-started&quot;&gt;Getting Started&lt;/h2&gt;

&lt;p&gt;Commonly Terraform module contains the next items: input variables, output variables and main service definition. There are three files from &lt;a href=&quot;/articles/how-to-deploy-applications-to-digitalocean-with-terraform/&quot;&gt;previous article&lt;/a&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;variables.tf&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;credentials.tf&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;service.tf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create a directory for your future Terraform module &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wordpress/&lt;/code&gt; and let’s start with files of service definition. Rename &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;service.tf&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.tf&lt;/code&gt;.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/7ccc2d5272d6ae4ec1736f1af86fe70a.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;&lt;small&gt;* since droplet &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt; is required argument, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;${coalesce(var.droplet_name, var.domain)}&quot;&lt;/code&gt;. It returns the first non-empty value from provided arguments, so you are able to give custom droplet name, otherwise it will be the same as a domain name.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Next create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;provider.tf&lt;/code&gt; with provider definition:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/9cabff24807001150ca0863ab36158ba.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;&lt;small&gt;* we skip token argument and will provide it via environment variable&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;At this moment there are complete service and provider definitions. But no module outputs and no input variables are defined, so do it.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/277da253ab62fd48d3d3b060897e12fc.js&quot;&gt; &lt;/script&gt;

&lt;h4 id=&quot;digitalocean-wordpress-specific-setup&quot;&gt;DigitalOcean WordPress specific setup&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;DigitalOcean’s WordPress One-Click application requires few manual steps to make website available for the public, such as providing ServerName for apache configuration.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Usually you need to login with SSH to droplet and then it runs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/opt/digitalocean/wp_setup.sh&lt;/code&gt; script to make required setup with interactive prompt, but we’ll perform the setup with Terraform.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/e9cec4ad8615187cbb40a4c71490dd62.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;Let’s take only things we need and put it into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user_data.sh&lt;/code&gt; file to apply it as &lt;a href=&quot;https://www.terraform.io/docs/providers/do/r/droplet.html#user_data&quot;&gt;user_data&lt;/a&gt; on droplet creation.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/ec412367e4e02a0248635281b35fea7b.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;But there is no domain we want to set for the droplet. Also we can’t easily pass Terraform variable into this script. Terraform has a solution for it called &lt;a href=&quot;https://www.terraform.io/docs/providers/template/d/file.html&quot;&gt;template_file&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Rename &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user_data.sh&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user_data.tpl&lt;/code&gt; and replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$dom&lt;/code&gt; bash variable with Terraform’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;${domain}&lt;/code&gt; variable on line 7.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/220d736c123d7d80391920aeb7847941.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;Now, create one more file to describe this template &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user_data.tf&lt;/code&gt;.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/f826b1f81f9fa47b2985e367517620be.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;The latest thing is to use created &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;template_file&lt;/code&gt; as a droplet’s user_data.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/af83e278e447f9ea1b5e8519d6cef7ed.js&quot;&gt; &lt;/script&gt;

&lt;h2 id=&quot;distribution&quot;&gt;Distribution&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;I prefer to use GitHub to store and distribute modules.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You need to create a GitHub repository and just put all files into it.&lt;/p&gt;

&lt;p&gt;Check out &lt;a href=&quot;https://github.com/sergeykuzmich/tfmodule-do_wordpress&quot;&gt;&lt;strong&gt;the repo&lt;/strong&gt;&lt;/a&gt; with this configuration.&lt;/p&gt;

&lt;p&gt;There are few more ways to distribute modules. You can check it out on &lt;a href=&quot;https://www.terraform.io/docs/modules/sources.html&quot;&gt;HashiCorp Docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;usage&quot;&gt;Usage&lt;/h2&gt;

&lt;p&gt;Create directory for your project and create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;service.tf&lt;/code&gt; file inside.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/c370c470301064480c023c2f1e37e380.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;That’s it! You just need to provide valid credentials to perform this action.&lt;/p&gt;

&lt;h4 id=&quot;credentials&quot;&gt;Credentials&lt;/h4&gt;

&lt;p&gt;I use the following way to provide credentials for Terraform:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;a file with the actual credentials as environment variables (somewhere in the user’s home directory):
 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;export DIGITALOCEAN_TOKEN=8d50ac...059828b&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.env&lt;/code&gt; file in the Terraform configuration directory to wrap environment variables in a Terraform-friendly way:
 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;export TF_VAR_token=$DIGITALOCEAN_TOKEN&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The DigitalOcean provider requires the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;token&lt;/code&gt; argument as an access_key, so I wrap &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$DIGITALOCEAN_TOKEN&lt;/code&gt; into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$TF_VAR_token=$DIGITALOCEAN_TOKEN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, to apply any changes I just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source ~/do_credentials&lt;/code&gt; file (the first one) and run Terraform commands.&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/sergeykuzmich/tfmodule-do_wordpress&quot;&gt;Terraform Module - DigitalOcean One-Click WordPress Application&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.terraform.io/docs/modules/index.html&quot;&gt;Terraform Modules&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.terraform.io/docs/configuration/environment-variables.html#tf_var_name&quot;&gt;Terraform Environment Variables&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.terraform.io/docs/configuration/interpolation.html&quot;&gt;Terraform Interpolation Syntax&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

    &lt;p&gt;&lt;a href=&quot;https://kuzmi.ch/notes/terraform-module-example/&quot;&gt;Terraform Module Example&lt;/a&gt; was originally published by Sergey Kuzmich at &lt;a href=&quot;https://kuzmi.ch&quot;&gt;Digital Moleskine&lt;/a&gt; on January 29, 2019.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[Facebook Enforces HTTPS - Developer's Thoughts]]></title>
  <link rel="alternate" type="text/html" href="https://kuzmi.ch/blog/facebook-enforces-https-developers-thoughts/" />
  <id>https://kuzmi.ch/blog/facebook-enforces-https---developers-thoughts</id>
  <published>2018-05-21T00:00:00+00:00</published>
  <updated>2018-05-21T00:00:00+00:00</updated>
  <author>
    <name>Sergey Kuzmich</name>
    <uri>https://kuzmi.ch</uri>
    <email>contact@kuzmi.ch</email>
  </author>
  <content type="html">
    &lt;blockquote&gt;
  &lt;p&gt;This blog post is a cry from the heart. I don’t know why, but there are more and more people who don’t even try to think before doing anything.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some time ago Facebook introduced new requirements for API applications. They want you to use only HTTPS as a transfer protocol to protect sensitive data from insecure transfer. I’m happy about it, but as usual they half-assed it.&lt;/p&gt;

&lt;p&gt;Most of the applications I have developed are deployed to AWS or similar providers and use the following infrastructure scheme:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/application-architecture.png&quot;&gt;&lt;img src=&quot;/images/application-architecture.png&quot; alt=&quot;Application Infrastructure&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, I handle the secure connection with a load balancer and the application doesn’t even know about the existence of the HTTPS connection. It just works.
But locally developers don’t have a load balancer and it’s okay to communicate with the application over HTTP.&lt;/p&gt;

&lt;p&gt;Facebook enforces HTTPS for applications and doesn’t allow it to be turned off even if the application isn’t published for users. Okay, there is another option: Facebook gives the ability to make a kind of ‘test’ version of an existing application with different API keys and secrets. But even in a test application you are still forced to use HTTPS. So you can’t check Facebook login without HTTPS enabled. What the heck are you doing, Facebook?&lt;/p&gt;

&lt;p&gt;I know that I can make a self-signed certificate or use a free certificate from &lt;a href=&quot;https://letsencrypt.org/&quot;&gt;Let’s Encrypt&lt;/a&gt;, but why should I mess with it if I can resolve it on the infrastructure layer? With this enforcement developers need to “implement” a local “secure” environment. Seriously?&lt;/p&gt;

&lt;h3 style=&quot;text-align:center&quot;&gt;🔥 Facebook burn in Hell! 🔥&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;UPD January 24, 2019:&lt;/strong&gt; It is possible to use HTTP protocol for applications in development mode, but only with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost&lt;/code&gt; domain.&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;https://kuzmi.ch/blog/facebook-enforces-https-developers-thoughts/&quot;&gt;Facebook Enforces HTTPS - Developer&apos;s Thoughts&lt;/a&gt; was originally published by Sergey Kuzmich at &lt;a href=&quot;https://kuzmi.ch&quot;&gt;Digital Moleskine&lt;/a&gt; on May 21, 2018.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[How to Deploy Applications to DigitalOcean with Terraform]]></title>
  <link rel="alternate" type="text/html" href="https://kuzmi.ch/notes/how-to-deploy-applications-to-digitalocean-with-terraform/" />
  <id>https://kuzmi.ch/notes/how-to-deploy-applications-to-digitalocean-with-terraform</id>
  <published>2018-03-14T00:00:00+00:00</published>
  <updated>2018-03-14T00:00:00+00:00</updated>
  <author>
    <name>Sergey Kuzmich</name>
    <uri>https://kuzmi.ch</uri>
    <email>contact@kuzmi.ch</email>
  </author>
  <content type="html">
    &lt;blockquote&gt;
  &lt;p&gt;I like &lt;a href=&quot;https://m.do.co/c/fac05d89f2e5&quot;&gt;DigitalOcean&lt;/a&gt; as a cloud servers provider because it is pretty simple and doesn’t cost too much. It already has complete enough infrastructure things for small websites and large services.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;DigitalOcean has a clean user-friendly Control Panel UI to manage droplets, networks and so on. I used to configure applications manually, but there is a way to do it much faster and more reliably.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://www.terraform.io&quot;&gt;Terraform&lt;/a&gt; is a tool which allows to define any kind of infrastructure as a simple text document and deploy or update it with a single command.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;getting-started&quot;&gt;Getting Started&lt;/h2&gt;

&lt;p&gt;Every single web application has at least two things: &lt;strong&gt;domain&lt;/strong&gt; and &lt;strong&gt;server&lt;/strong&gt;. It means if you need to run the simplest website you need to create a server (DigitalOcean calls it &lt;strong&gt;droplets&lt;/strong&gt;) and map a domain to server’s IP address.&lt;/p&gt;

&lt;h5 id=&quot;droplet-definition&quot;&gt;Droplet Definition&lt;/h5&gt;

&lt;p&gt;You can create droplet with DigitalOcean Control Panel with ‘Create Droplet’ button, filling droplet’s name, choosing droplet type, region and size.&lt;/p&gt;

&lt;p&gt;Droplet definition with Terraform looks the next way:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/20319e9997fd7a7394d2a6702e047b98.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;&lt;small&gt;* &lt;em&gt;&lt;a href=&quot;https://www.terraform.io/docs/providers/do/r/droplet.html&quot;&gt;The list of all available droplet arguments.&lt;/a&gt;&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h5 id=&quot;domain-definition&quot;&gt;Domain Definition&lt;/h5&gt;

&lt;p&gt;The same things with adding domains. You can do it via DigitalOcean Control Panel or provide Terraform configuration:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/b3d1284ad178afb797005f7cd1d6353b.js&quot;&gt; &lt;/script&gt;

&lt;h4 id=&quot;deployment&quot;&gt;Deployment&lt;/h4&gt;

&lt;p&gt;Begin with creating directory to store Terraform configuration files. Then create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;service.tf&lt;/code&gt; (the filename can be anything you want) file in this directory and put domain and droplet definitions into.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/5888fa06e5b8fc8afab15e44c95c13e1.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;Domain &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ip_address&lt;/code&gt; uses a reference to the defined droplet as a value to automatically map the droplet to the domain.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Terraform is smart enough to understand that domain can’t be created without droplet’s IP address, so first it will create a droplet, and then it will give correct ip_address for the domain value.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Almost done. The last thing to do is to set provider credentials, so create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;credentials.tf&lt;/code&gt; file in the same directory and put next lines into:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/874254f5ff8616f7dd7679b443012b79.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;&lt;em&gt;You need to generate a DigitalOcean API token in the &lt;a href=&quot;https://cloud.digitalocean.com/settings/api/tokens&quot;&gt;API&lt;/a&gt; section of the &lt;a href=&quot;https://cloud.digitalocean.com&quot;&gt;DigitalOcean Control Panel&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h5 id=&quot;here-we-go&quot;&gt;Here we go…&lt;/h5&gt;

&lt;p&gt;Initialize Terraform provider with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform init&lt;/code&gt;. It downloads required provider files and initializes Terraform configuration directory (almost like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git init&lt;/code&gt; command initializes repository in working directory).&lt;/p&gt;

&lt;p&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform plan&lt;/code&gt;, it gives you the list of actions that Terraform will performe on DigitalOcean. With current infrastructure definition you should see output like this:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/cf6df72392df8c08ff253299c5a89a5f.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;It’s time to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform apply&lt;/code&gt;. Confirm actions and you’ll get defined resources on your DigitalOcean account. Check it out on Control Panel.&lt;/p&gt;

&lt;h5 id=&quot;roll-back&quot;&gt;Roll-back&lt;/h5&gt;

&lt;p&gt;To destroy this infrastructure just run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform destroy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Don’t be afraid to lose other droplets and services on your account by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform destroy&lt;/code&gt;. Terraform stores defined and deployed resources in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform.tfstate&lt;/code&gt; file, so it will only remove resources defined and deployed from this working directory.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;re-usable-configuration&quot;&gt;Re-usable Configuration&lt;/h2&gt;

&lt;p&gt;At this moment dynamic values (such as domain name, droplet type and size) are directly used in resource definitions. It is ok, but if you want to make re-usable configuration you need to export values to variables.&lt;/p&gt;

&lt;h5 id=&quot;variable-definition&quot;&gt;Variable Definition&lt;/h5&gt;

&lt;p&gt;Terraform variable looks the next way:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/69ffccc242f72095ba666debf2aebfd8.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;&lt;small&gt;* &lt;em&gt;Type, description and default variables are optional arguments.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h4 id=&quot;export-variables&quot;&gt;Export Variables&lt;/h4&gt;

&lt;p&gt;Create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;variables.tf&lt;/code&gt; file and think about which arguments &lt;strong&gt;may be changed&lt;/strong&gt; and which arguments &lt;strong&gt;should be changed&lt;/strong&gt; in similar deployments (applications).&lt;/p&gt;

&lt;p&gt;For example, &lt;strong&gt;droplet size&lt;/strong&gt; and &lt;strong&gt;image&lt;/strong&gt; can be the same for different projects, so you can provide default value for this arguments, but &lt;strong&gt;domain name&lt;/strong&gt; can’t be the same for different projects, so you just need to define that you’re requiring this argument but it won’t have default value.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/362b58ae2a867cfc17da945bb0d90d0e.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;Now replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;credentials.tf&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;service.tf&lt;/code&gt; files values with new created variables:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/eb6e078567dbc6e361e0a8e856edfbda.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;Next you should provide values for variables when making &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform apply&lt;/code&gt;. Values can be passed by:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#environment-variables&quot;&gt;Environment Variables&lt;/a&gt;;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#interactive-prompt&quot;&gt;Interactive Prompt&lt;/a&gt;;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#variables-files&quot;&gt;Variable Files&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;environment-variables&quot;&gt;Environment Variables&lt;/h5&gt;

&lt;p&gt;You can provide values via environment. The name of the environment variable must start with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TF_VAR_&lt;/code&gt; followed by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;variable_name&lt;/code&gt;, and the value is the value of the variable.&lt;/p&gt;

&lt;p&gt;For example, variables above can be set by:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/ac4e2036d4673d04001b2cbb57887d60.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/de6ebcfbbba9eec6fc085c3306b9ddcd.js&quot;&gt; &lt;/script&gt;

&lt;h5 id=&quot;interactive-prompt&quot;&gt;Interactive Prompt&lt;/h5&gt;

&lt;p&gt;Alternatively you can provide values just by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform apply&lt;/code&gt; and Terraform will ask for it (which does not have defaults) interactively:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/d1227a4a9921d06f5b94b729af95ba53.js&quot;&gt; &lt;/script&gt;

&lt;h5 id=&quot;variable-files&quot;&gt;Variable Files&lt;/h5&gt;

&lt;p&gt;And the last way to provide values is files which match &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform.tfvars&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*.auto.tfvars&lt;/code&gt; in the configuration directory, Terraform automatically loads them to populate variables. For example:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/9de54d34d73627614a5d3395eb79d2ba.js&quot;&gt; &lt;/script&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;With this setup you can deploy a big number of services in a few minutes. Here is only example of the easiest and the smallest infrastructure, but with Terraform you can describe infrastructure of any complexity and size.&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://m.do.co/c/fac05d89f2e5&quot;&gt;DigitalOcean&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.terraform.io&quot;&gt;Terraform&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

    &lt;p&gt;&lt;a href=&quot;https://kuzmi.ch/notes/how-to-deploy-applications-to-digitalocean-with-terraform/&quot;&gt;How to Deploy Applications to DigitalOcean with Terraform&lt;/a&gt; was originally published by Sergey Kuzmich at &lt;a href=&quot;https://kuzmi.ch&quot;&gt;Digital Moleskine&lt;/a&gt; on March 14, 2018.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[WordPress Plugin Deployment Using GitHub and Travis CI]]></title>
  <link rel="alternate" type="text/html" href="https://kuzmi.ch/notes/wordpress-plugin-and-theme-development-using-git-only/" />
  <id>https://kuzmi.ch/notes/wordpress-plugin-and-theme-development-using-git-only</id>
  <published>2018-01-07T00:00:00+00:00</published>
  <updated>2018-01-07T00:00:00+00:00</updated>
  <author>
    <name>Sergey Kuzmich</name>
    <uri>https://kuzmi.ch</uri>
    <email>contact@kuzmi.ch</email>
  </author>
  <content type="html">
    &lt;blockquote&gt;
  &lt;p&gt;I love &lt;a href=&quot;https://wordpress.org&quot;&gt;WordPress&lt;/a&gt; and I’m sure it is the best solution for corporate websites and personal blogs. I’m happy to contribute code for WordPress, create plugins and themes. I’ve made a few public plugins: &lt;a href=&quot;https://wordpress.org/plugins/inline-spoilers/&quot;&gt;Inline Spoilers&lt;/a&gt; and &lt;a href=&quot;https://wordpress.org/plugins/sanitize-cyrillic/&quot;&gt;Sanitize Cyrillic&lt;/a&gt;.&lt;br /&gt;
But there is one unpleasant thing that everytime stopped me. WordPress wants you to use SVN to store and deploy public plugins and themes but development with SVN is too annoying for me. There are topics how to use git for WordPress but it is all about git-svn.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;I’m expecting that you already have a submitted WordPress plugin and just want to avoid SVN things, otherwise, please, take a look at &lt;a href=&quot;https://developer.wordpress.org/plugins/wordpress-org/&quot;&gt;this guide&lt;/a&gt; and welcome back.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;step-1-travis-ci-configuration&quot;&gt;Step 1: Travis CI Configuration&lt;/h2&gt;

&lt;p&gt;At this point, you have plugin’s code hosted on &lt;a href=&quot;https://developer.wordpress.org/plugins/wordpress-org/how-to-use-subversion/&quot;&gt;WordPress SVN&lt;/a&gt; and &lt;a href=&quot;https://github.com&quot;&gt;GitHub&lt;/a&gt;. Next you need to create configuration and describe all things you want to be executed by &lt;a href=&quot;https://travis-ci/org&quot;&gt;Travis&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Start with creating ‘dummy’ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.travis.yml&lt;/code&gt; file in your repository to tell Travis what to do:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/112d65c7998be8d3644c641b247abafb.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;Travis will proceed all future commits and pull requests for the repository with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.travis.yml&lt;/code&gt; file.&lt;/p&gt;

&lt;h2 id=&quot;step-2-configure-wordpress-plugin-assets&quot;&gt;Step 2: Configure WordPress Plugin Assets&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;WordPress has custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assets/&lt;/code&gt; directory in plugin SVN repository to store plugin page assets such as screenshots, icons, and etc.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assets/&lt;/code&gt; directory and put empty &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitkeep&lt;/code&gt; file into to make sure it exists even without any file inside.&lt;/p&gt;

&lt;h2 id=&quot;step-3-write-deployment-script&quot;&gt;Step 3: Write Deployment Script&lt;/h2&gt;

&lt;h3 id=&quot;️--thats-where-the-magic-begins&quot;&gt;🤹‍♀️ ~ that’s where the magic begins…&lt;/h3&gt;

&lt;p&gt;To avoid SVN stuff just put it to shell script, create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deploy/deploy.sh&lt;/code&gt; script file with next contents:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/f598ea30e0d791e1eef9f96d941e8b2f.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;&lt;small&gt;* &lt;em&gt;Used global variables will be covered a little bit later.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;It will do all deployment stuff, you just need to execute this script.&lt;/p&gt;

&lt;p&gt;Wait…&lt;/p&gt;

&lt;p&gt;Actually you don’t need to execute script to deploy a new release. Travis will do that for you.&lt;/p&gt;

&lt;h2 id=&quot;step-4-enable-travis-ci-deployment&quot;&gt;Step 4: Enable Travis CI Deployment&lt;/h2&gt;

&lt;p&gt;I think you don’t need to deploy each new commit, you need to tell Travis to execute deployment script only on “some” specific events.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I’d prefer using tags to specify plugin releases. I want Travis to submit a new plugin version to WordPress each time I push a new git tag.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To enable deployment on git tags provide next configuration to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.travis.yml&lt;/code&gt;:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/3763eb6231c69ea66882b16f05ef9f97.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;&lt;small&gt;* &lt;em&gt;Travis sees git tags the same way as branches.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I like to use &lt;a href=&quot;http://semver.org&quot;&gt;semver&lt;/a&gt; for projects, I’ve set semver regular expression as a branch filter to allow deployments only for this kind of tags.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;environment-variables&quot;&gt;Environment variables&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;$SVN_REPOSITORY&lt;/strong&gt; - WordPress plugin SVN repository URL.&lt;br /&gt;
&lt;strong&gt;$TRAVIS_TAG&lt;/strong&gt; - Tag label. &lt;em&gt;(This variable is fetched and provided by Travis from GitHub)&lt;/em&gt;&lt;br /&gt;
&lt;strong&gt;$SVN_USERNAME&lt;/strong&gt; - Encrypted WordPress account username.&lt;br /&gt;
&lt;strong&gt;$SVN_PASSWORD&lt;/strong&gt; - Encrypted WordPress account password.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How to &lt;a href=&quot;https://docs.travis-ci.com/user/environment-variables/#Defining-encrypted-variables-in-.travis.yml&quot;&gt;define encrypted variables in .travis.yml&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Now you have a complete setup for an automated plugin deployment process.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;With this way you don’t need to do any SVN specific actions. You may just develop plugins and when you’re ready to give users a new version just push tag for commit you want to deploy. Travis will do all required stuff and after a few minutes you’ll see new deployed version on WordPress website.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://wordpress.org&quot;&gt;WordPress&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.wordpress.org/plugins/wordpress-org/&quot;&gt;WordPress Plugin Handbook&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com&quot;&gt;GitHub&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://travis-ci/org&quot;&gt;Travis&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://semver.org&quot;&gt;Semantic Versioning&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

    &lt;p&gt;&lt;a href=&quot;https://kuzmi.ch/notes/wordpress-plugin-and-theme-development-using-git-only/&quot;&gt;WordPress Plugin Deployment Using GitHub and Travis CI&lt;/a&gt; was originally published by Sergey Kuzmich at &lt;a href=&quot;https://kuzmi.ch&quot;&gt;Digital Moleskine&lt;/a&gt; on January 07, 2018.&lt;/p&gt;
  </content>
</entry>

</feed>