<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>tekkie</title>
    <description>Technology blog by Ain Tohvri. Contributor &amp; maintainer of various Free and Open Source Software projects.</description>
    <link>https://tekkie.dev/</link>
    <atom:link href="https://tekkie.dev/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Mon, 17 Aug 2026 21:02:48 +0000</pubDate>
    <lastBuildDate>Mon, 17 Aug 2026 21:02:48 +0000</lastBuildDate>
    <generator>Jekyll v4.4.1</generator>
    
      <item>
        <title>Getting indexed without waiting for the crawler</title>
        <description>&lt;p&gt;Publishing is only half of the job. A post that nobody can find might as well not exist, and for a static site the gap between deploying a page and a search engine noticing it is entirely out of your hands. Both Google and Bing offer &lt;abbr title=&quot;Application Programming Interfaces&quot;&gt;APIs&lt;/abbr&gt; to close that gap by pushing URLs at them directly — and both are fiddly enough that hardly anyone bothers. &lt;a href=&quot;https://crates.io/crates/indeks&quot; rel=&quot;nofollow external&quot;&gt;indeks&lt;/a&gt; is a small Rust &lt;abbr title=&quot;Command Line Interface&quot;&gt;CLI&lt;/abbr&gt; that does the bothering for you.
&lt;!--more--&gt;&lt;/p&gt;

&lt;h2 id=&quot;waiting-versus-telling&quot;&gt;Waiting versus telling&lt;/h2&gt;

&lt;p&gt;The conventional route is passive. You publish a sitemap, you reference it in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;robots.txt&lt;/code&gt;, you register it in Search Console, and then you wait for a crawler to come round. On a site that changes a few times a year that is perfectly adequate. On one that changes weekly, the lag is irritating: you have fixed a typo in a heading, or corrected something factually wrong, and the version the world sees is the old one for days.&lt;/p&gt;

&lt;p&gt;Crawl budget is the underlying constraint. Search engines allocate a finite amount of attention per host and spend it according to how often a site has historically changed. A small site that publishes rarely gets visited rarely, which is precisely the site whose occasional new page most needs announcing.&lt;/p&gt;

&lt;p&gt;The active route is to tell the engine yourself. Two protocols cover most of the ground:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;IndexNow&lt;/strong&gt; — an open protocol, originally from Microsoft, now with several participants sharing submissions among them. You &lt;abbr title=&quot;HTTP POST request method&quot;&gt;POST&lt;/abbr&gt; a list of URLs with a key.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Google Indexing &lt;abbr title=&quot;Application Programming Interface&quot;&gt;API&lt;/abbr&gt;&lt;/strong&gt; — Google’s own endpoint, authenticated with a service account tied to a verified property.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-indeks-does&quot;&gt;What indeks does&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;indeks&lt;/code&gt; is a single command that takes URLs, sitemaps, or both, and submits them to one of those two engines.&lt;/p&gt;

&lt;p&gt;Installation became easier as of version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v0.1.1&lt;/code&gt; that shipped the installer:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/ain/indeks/main/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For developers that have Rust installed, using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo&lt;/code&gt; directly is of course the easiest:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cargo build &lt;span class=&quot;nt&quot;&gt;--release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The shape of an invocation is:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;indeks &amp;lt;google|bing&amp;gt; [--url &amp;lt;URL&amp;gt;]... [--sitemap &amp;lt;SITEMAP&amp;gt;]... --credentials &amp;lt;TOKEN_OR_PATH&amp;gt; [--dry-run] [--verbose]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first positional argument picks the engine — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google&lt;/code&gt; for the Indexing &lt;abbr title=&quot;Application Programming Interface&quot;&gt;API&lt;/abbr&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bing&lt;/code&gt; for IndexNow — and one is required. Everything else is a flag.&lt;/p&gt;

&lt;h2 id=&quot;feeding-it-urls&quot;&gt;Feeding it URLs&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--url&lt;/code&gt; takes a single absolute &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https&lt;/code&gt; &lt;abbr title=&quot;Uniform Resource Locator&quot;&gt;URL&lt;/abbr&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--sitemap&lt;/code&gt; takes a sitemap, which may be a remote &lt;abbr title=&quot;Uniform Resource Locator&quot;&gt;URL&lt;/abbr&gt;, a local file path, or a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;file://&lt;/code&gt; &lt;abbr title=&quot;Uniform Resource Locator&quot;&gt;URL&lt;/abbr&gt; — useful when you want to submit a sitemap your build just generated but has not deployed yet.&lt;/p&gt;

&lt;p&gt;Both are repeatable and can be mixed freely, so long as at least one is present:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;indeks bing &lt;span class=&quot;nt&quot;&gt;--url&lt;/span&gt; https://example.com/a &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;--sitemap&lt;/span&gt; tests/fixtures/sitemap.xml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;--sitemap&lt;/span&gt; https://example.com/sitemap.xml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;--credentials&lt;/span&gt; abcdef0123456789
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A &lt;abbr title=&quot;Uniform Resource Locator&quot;&gt;URL&lt;/abbr&gt; that turns up more than once — passed twice, or passed directly and also present in a sitemap — is submitted once. That matters more than it sounds, because Google’s daily quota counts requests, not distinct URLs.&lt;/p&gt;

&lt;p&gt;Sitemap parsing is deliberately strict. A sitemap must be valid &lt;abbr title=&quot;Extensible Markup Language&quot;&gt;XML&lt;/abbr&gt; in the &lt;a href=&quot;https://www.sitemaps.org/protocol.html&quot; rel=&quot;nofollow external&quot;&gt;sitemaps.org&lt;/a&gt; format with at least one &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;urlset &amp;gt; url &amp;gt; loc&lt;/code&gt;, and every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;loc&lt;/code&gt; an absolute &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https&lt;/code&gt; &lt;abbr title=&quot;Uniform Resource Locator&quot;&gt;URL&lt;/abbr&gt;.&lt;/p&gt;

&lt;h2 id=&quot;failing-before-the-network&quot;&gt;Failing before the network&lt;/h2&gt;

&lt;p&gt;The feature I find myself using most is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--dry-run&lt;/code&gt;, which validates everything and contacts nothing:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ indeks bing --url https://example.com/a \
              --sitemap tests/fixtures/sitemap.xml \
              --credentials abcdef0123456789 \
              --dry-run
Dry run: no external system will be contacted.
Engine: Bing IndexNow
Credentials: token

URLs (1):
  https://example.com/a

Sitemaps (1):
  tests/fixtures/sitemap.xml — local file, 3 URLs
    https://example.com/
    https://example.com/about
    https://example.com/contact

Input is valid. Remove --dry-run to submit.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Local sitemaps are read and fully parsed during a dry run; remote ones are not fetched, so only the &lt;abbr title=&quot;Uniform Resource Locator&quot;&gt;URL&lt;/abbr&gt; itself is checked. You see the expansion before committing to it.&lt;/p&gt;

&lt;p&gt;Validation reports every problem at once rather than one per run, which is the difference between one round trip and four:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ indeks google --url /relative --sitemap ftp://x.example --credentials ./nope.json
error: --url /relative: not an absolute URL
error: --sitemap ftp://x.example: scheme `ftp` is not supported; use http, https or a file path
error: --credentials ./nope.json: no such file
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--credentials&lt;/code&gt; is required even with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--dry-run&lt;/code&gt;, which looks pedantic until you consider that a pre-flight check passing on input that a real run would reject is worth nothing. A credentials file is read and its &lt;abbr title=&quot;JavaScript Object Notation&quot;&gt;JSON&lt;/abbr&gt; parsed during a dry run, since neither step touches the network.&lt;/p&gt;

&lt;h2 id=&quot;credentials-per-engine&quot;&gt;Credentials, per engine&lt;/h2&gt;

&lt;p&gt;For &lt;strong&gt;Google&lt;/strong&gt;, the credential is either a service-account &lt;abbr title=&quot;JavaScript Object Notation&quot;&gt;JSON&lt;/abbr&gt; file or a bare OAuth access token. The service account must be an owner of the property in Search Console and the Indexing &lt;abbr title=&quot;Application Programming Interface&quot;&gt;API&lt;/abbr&gt; must be enabled for its project — a 403 tells you which.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;Bing&lt;/strong&gt;, the credential is an IndexNow key: 8–128 characters of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a-z&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A-Z&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0-9&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&lt;/code&gt;, passed directly or held in a &lt;abbr title=&quot;JavaScript Object Notation&quot;&gt;JSON&lt;/abbr&gt; file as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;key&lt;/code&gt; field:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;abcdef0123456789&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;IndexNow also requires that key to be readable at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://&amp;lt;host&amp;gt;/&amp;lt;key&amp;gt;.txt&lt;/code&gt; for every host you submit. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;indeks&lt;/code&gt; cannot do that for you, but a 403 names the exact &lt;abbr title=&quot;Uniform Resource Locator&quot;&gt;URL&lt;/abbr&gt; the file has to appear at — the single most common way to get this wrong. URLs are grouped by host, since one request may only carry one, and each host’s batch is sent separately, so one host failing does not stop the others.&lt;/p&gt;

&lt;h2 id=&quot;quotas-and-what-a-429-actually-means&quot;&gt;Quotas, and what a 429 actually means&lt;/h2&gt;

&lt;p&gt;This is where the tool earns its keep. Google applies two limits per Cloud project:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Quota&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Default&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Publish requests per day&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;200, resetting at midnight Pacific&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Requests per minute, all endpoints&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;380&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;A 429 names which one ran out, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;indeks&lt;/code&gt; reads that instead of guessing. If it is the daily limit, waiting cannot help before midnight Pacific, so the run stops immediately and the remaining URLs are reported as not attempted. If it is the per-minute limit — or a 429 naming no metric at all — the limit clears on its own, so the request is retried with a doubling backoff of 10s, 20s and 40s, honouring &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Retry-After&lt;/code&gt; when the server sends one. Only if it still fails does the run stop.&lt;/p&gt;

&lt;p&gt;Treating those two cases identically is the obvious implementation and the wrong one: it either burns forty seconds waiting for a limit that resets tomorrow, or gives up on one that would have cleared by itself.&lt;/p&gt;

&lt;h2 id=&quot;output-you-can-paste-into-a-bug-report&quot;&gt;Output you can paste into a bug report&lt;/h2&gt;

&lt;p&gt;By default, success logs a response code and a &lt;abbr title=&quot;Uniform Resource Locator&quot;&gt;URL&lt;/abbr&gt;, and failure logs the code and the error:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;indeks bing &lt;span class=&quot;nt&quot;&gt;--url&lt;/span&gt; https://example.com/a &lt;span class=&quot;nt&quot;&gt;--credentials&lt;/span&gt; abcdef0123456789
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;403] https://example.com/a: the key was not accepted&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; it must be readable at https://example.com/abcdef0123456789.txt
1 of 1 URLs were not accepted

Please consider using &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--verbose&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; to find out more
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--verbose&lt;/code&gt; logs connection setup, &lt;abbr title=&quot;Transport Layer Security&quot;&gt;TLS&lt;/abbr&gt; negotiation, headers, bodies and timings.&lt;/p&gt;

&lt;p&gt;Verbose output is designed to be pasteable as-is. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Authorization&lt;/code&gt; header is always redacted. So if you do find a bug, feel free to &lt;a href=&quot;https://github.com/ain/indeks/issues/new&quot;&gt;report a new issue&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Exit codes are conventional:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt; when everything was submitted or a dry run passed&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt; when input was fine but submission failed&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt; for invalid arguments or an unreadable sitemap&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enough to branch on in a deploy script.&lt;/p&gt;

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

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;indeks&lt;/code&gt; is on &lt;a href=&quot;https://crates.io/crates/indeks&quot; rel=&quot;nofollow external&quot;&gt;crates.io&lt;/a&gt; and the source is on &lt;a href=&quot;https://github.com/ain/indeks&quot; rel=&quot;nofollow external&quot;&gt;GitHub&lt;/a&gt;, released under GPL-3.0-only. It is roughly 2,400 lines of Rust on the 2024 edition, and the test suite reaches nothing outside the machine it runs on.&lt;/p&gt;

</description>
        <pubDate>Thu, 30 Jul 2026 00:00:00 +0000</pubDate>
        <link>https://tekkie.dev/rust/getting-indexed-without-waiting-for-the-crawler</link>
        <guid isPermaLink="true">https://tekkie.dev/rust/getting-indexed-without-waiting-for-the-crawler</guid>
        
        <category>indexnow</category>
        
        <category>seo</category>
        
        <category>sitemap</category>
        
        <category>cli</category>
        
        <category>google</category>
        
        <category>bing</category>
        
        <category>developer</category>
        
        
        <category>rust</category>
        
      </item>
    
      <item>
        <title>Linting Fastly VCL with Falco GitHub Action</title>
        <description>&lt;p&gt;When pushing &lt;abbr title=&quot;Varnish Configuration Language&quot;&gt;VCL&lt;/abbr&gt; files to Fastly, a missing semicolon or comma can lead to a failure. The configuration will fail to activate, you have to check why and what errored. Typos happen. It’s linter’s job to catch them and let us fail fast.
&lt;!--more--&gt;&lt;/p&gt;

&lt;h2 id=&quot;fastly-vcl&quot;&gt;Fastly &lt;abbr title=&quot;Varnish Configuration Language&quot;&gt;VCL&lt;/abbr&gt;&lt;/h2&gt;

&lt;p&gt;Fastly is one of the world’s leading &lt;abbr title=&quot;Content Delivery Networks&quot;&gt;CDNs&lt;/abbr&gt; and Edge Computing platforms that helps to accelerate a fairly big chunk of today’s internet. From eCommerce to streaming solutions, if they’re performing well, they’re likely to have been built around Fastly&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;What makes Fastly great is its flexibility powered by the possibility to use custom Varnish configuration files (VCLs) to route traffic, alter and handle requests, or even compute at the edge. All close to the end user.&lt;/p&gt;

&lt;h2 id=&quot;falco-vcl-parser&quot;&gt;Falco &lt;abbr title=&quot;Varnish Configuration Language&quot;&gt;VCL&lt;/abbr&gt; parser&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/ysugimoto/falco&quot; rel=&quot;nofollow external&quot;&gt;Falco&lt;/a&gt; is an Open Source &lt;abbr title=&quot;Varnish Configuration Language&quot;&gt;VCL&lt;/abbr&gt; parser that is built against Fastly’s implementation of Varnish.&lt;/p&gt;

&lt;p&gt;It has a comprehensive set of features one of which is dedicated to linting Fastly &lt;abbr title=&quot;Varnish Configuration Language&quot;&gt;VCL&lt;/abbr&gt;.&lt;/p&gt;

&lt;h2 id=&quot;linting-vcl-with-falco-github-action&quot;&gt;Linting &lt;abbr title=&quot;Varnish Configuration Language&quot;&gt;VCL&lt;/abbr&gt; with Falco GitHub Action&lt;/h2&gt;

&lt;p&gt;In order to automate the linting of Fastly &lt;abbr title=&quot;Varnish Configuration Language&quot;&gt;VCL&lt;/abbr&gt;, I’ve built the &lt;a href=&quot;https://github.com/ain/falco-github-action&quot; rel=&quot;nofollow external&quot;&gt;Falco GitHub Action&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Everyone who has worked with &lt;abbr title=&quot;Continuous Integration/Continous Deployment&quot;&gt;CI/CD&lt;/abbr&gt; pipelines on GitHub before, the configuration of the Falco GitHub Action is trivial and self-explanatory. You define the includes used by the &lt;abbr title=&quot;Varnish Configuration Language&quot;&gt;VCL&lt;/abbr&gt; file in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;options&lt;/code&gt; and the &lt;abbr title=&quot;Varnish Configuration Language&quot;&gt;VCL&lt;/abbr&gt; file to lint in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;target&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-vcl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; name: Lint VCL
  uses: ain&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;falco-github-action@v1
  with:
    subcommand: lint
    options: &lt;span class=&quot;s2&quot;&gt;&quot;-v -I test/vcl/includes&quot;&lt;/span&gt;
    target: test&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;vcl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;valid_with_include.vcl
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;ATTN: you can also lint &lt;abbr title=&quot;Access Control List&quot;&gt;ACL&lt;/abbr&gt; files! Linting has a lot of value against a missing comma or invalid &lt;abbr title=&quot;Classless Inter-Domain Routing&quot;&gt;CIDR&lt;/abbr&gt; notation in those big dictionaries with thousands of IP ranges.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://www.fastly.com/customers&quot; rel=&quot;nofollow external&quot;&gt;Fastly Customer Stories&lt;/a&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Thu, 23 Jan 2025 00:00:00 +0000</pubDate>
        <link>https://tekkie.dev/devops/fastly-vcl-linting</link>
        <guid isPermaLink="true">https://tekkie.dev/devops/fastly-vcl-linting</guid>
        
        <category>fastly</category>
        
        <category>varnish</category>
        
        <category>developer</category>
        
        <category>falco</category>
        
        <category>edge computing</category>
        
        <category>cdn</category>
        
        
        <category>devops</category>
        
      </item>
    
      <item>
        <title>MacBook Pro M1 looks like a developer machine</title>
        <description>&lt;p&gt;Apple’s new MacBook Pro delivers more than the new M1 chip. Something good is back for the developers.&lt;!--more--&gt;&lt;/p&gt;

&lt;h2 id=&quot;macbook-pros-keyboard&quot;&gt;MacBook Pro’s keyboard&lt;/h2&gt;

&lt;p&gt;Having tested the first MacBook Pro with Touch Bar back in 2017, it felt like &lt;a href=&quot;/apple/macbook/macbook-is-dropping-developers&quot;&gt;MacBook was dropping developers&lt;/a&gt;. Some essential ergonomics got disrupted esp. for the Vim-based devs.&lt;/p&gt;

&lt;p&gt;Today, not only has Apple made a prominent switch to their own M1 chips and tripled the performance&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; by doing so, they’ve also brought back the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Esc&lt;/code&gt; key. Did Apple’s designers listen to the critique? Perhaps.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/macbook-pro-m1-escape-key.jpg&quot; alt=&quot;New MacBook Pro M1 with Esc key&quot; width=&quot;375&quot; height=&quot;207&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;docker-desktop&quot;&gt;Docker Desktop&lt;/h2&gt;

&lt;h3 id=&quot;docker-and-apple-silicon&quot;&gt;Docker and Apple Silicon&lt;/h3&gt;

&lt;p&gt;Official support for M1 chip is there in Docker Desktop &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3.3.1&lt;/code&gt;&lt;sup id=&quot;fnref:3&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;Unfortunately no improvement in &lt;a href=&quot;/devops/docker-performance-on-mac&quot;&gt;Docker performance on macOS&lt;/a&gt; has been reported.&lt;/p&gt;

&lt;h3 id=&quot;docker-desktop-big-sur-and-intel&quot;&gt;Docker Desktop, Big Sur and Intel&lt;/h3&gt;

&lt;p&gt;Docker Desktop runs fine on old Intel-based MacBook Pros with macOS Big Sur. There’s no performance drop.&lt;/p&gt;

&lt;p&gt;Since the performance bottleneck for Docker on macOS is the file system, it is important to note that all versions since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.4.2.0&lt;/code&gt; feature &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gRPC FUSE&lt;/code&gt; for file sharing and are considerably faster than the legacy &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;osxfs&lt;/code&gt;, also on Big Sur.&lt;/p&gt;

&lt;h2 id=&quot;macbook-pro-m1-for-developers&quot;&gt;MacBook Pro M1 for developers&lt;/h2&gt;

&lt;p&gt;With Docker Desktop supporting MacBook Pro M1, it surely serves as a very performant platform for developers who need the mesh of microservices set up locally.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://www.apple.com/macbook-pro/&quot; rel=&quot;nofollow external&quot;&gt;MacBook Pro M1&lt;/a&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://www.macrumors.com/2020/11/11/m1-macbook-air-first-benchmark/&quot; rel=&quot;nofollow external&quot;&gt;Apple Silicon M1 Chip in MacBook Air Outperforms High-End 16-Inch MacBook Pro&lt;/a&gt; &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://www.docker.com/blog/released-docker-desktop-for-mac-apple-silicon/&quot; rel=&quot;nofollow external&quot;&gt;Released: Docker Desktop for Mac [Apple Silicon]&lt;/a&gt; &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Tue, 10 Nov 2020 00:00:00 +0000</pubDate>
        <link>https://tekkie.dev/apple/macbook/macbook-pro-m1-looks-like-developer-machine</link>
        <guid isPermaLink="true">https://tekkie.dev/apple/macbook/macbook-pro-m1-looks-like-developer-machine</guid>
        
        <category>macbookpro</category>
        
        <category>docker</category>
        
        <category>developer</category>
        
        <category>touchbar</category>
        
        <category>apple</category>
        
        
        <category>apple</category>
        
        <category>macbook</category>
        
      </item>
    
      <item>
        <title>Avoid DevOps disruption by Docker Hub changes</title>
        <description>&lt;p&gt;Docker will start rate-limiting image pulls from Docker Hub on November 1st, potentially halting a fair number of CI/CD pipelines.&lt;!--more--&gt;&lt;/p&gt;

&lt;h2 id=&quot;docker-dealing-with-network-egress&quot;&gt;Docker dealing with network egress&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;Roughly 30% of all downloads on Docker Hub come from only 1% of anonymous users&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not only has it a price tag, but it surely affects the overall performance of the world’s largest container registry&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; as well.&lt;/p&gt;

&lt;h2 id=&quot;new-subscription-plans&quot;&gt;New subscription plans&lt;/h2&gt;

&lt;p&gt;To control the unfair share of network egress, the structure of the subscription plans has been announced as follows:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Free plan – anonymous users: 100 pulls per 6 hours&lt;/li&gt;
  &lt;li&gt;Free plan – authenticated users: 200 pulls per 6 hours&lt;/li&gt;
  &lt;li&gt;Pro plan – unlimited&lt;/li&gt;
  &lt;li&gt;Team plan – unlimited&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;docker-hub-pull-authentication&quot;&gt;Docker Hub pull authentication&lt;/h2&gt;

&lt;p&gt;In most development teams 100 pulls per 6 hours is very likely to be insufficient, especially in teams that rely on Continuous Deployment.&lt;/p&gt;

&lt;p&gt;Nevertheless, many teams would also find 200 pulls in 6 hours sufficient, meaning that authenticating the pulls would do.&lt;/p&gt;

&lt;h3 id=&quot;docker-auth-on-circleci&quot;&gt;Docker auth on CircleCI&lt;/h3&gt;

&lt;p&gt;Job setup on CircleCI&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; is straightforward:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;docker&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;acme-private/private-image:321&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;auth&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;my-docker-hub-user&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$DOCKER_HUB_PASSWORD&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;pull-rate-limit-for-circleci-images&quot;&gt;Pull rate limit for CircleCI images&lt;/h4&gt;

&lt;p&gt;Many developers use pre-built CircleCI Docker images&lt;sup id=&quot;fnref:3&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci/ruby&lt;/code&gt;) to leverage CircleCI’s caching. Pulls are therefore much faster.&lt;/p&gt;

&lt;p&gt;It’s relevant to keep in mind that &lt;strong&gt;all CircleCI images count towards pull rate limit&lt;/strong&gt; as well.&lt;/p&gt;

&lt;h3 id=&quot;docker-auth-on-github-actions&quot;&gt;Docker auth on GitHub Actions&lt;/h3&gt;

&lt;p&gt;Workflow step configuration on GitHub Actions leverages &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker/login-action&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Login to Docker Hub&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;docker/login-action@v1&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ secrets.DOCKER_HUB_USERNAME }}&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;caching-docker-images-on-gitlab-ci&quot;&gt;Caching Docker images on GitLab CI&lt;/h3&gt;

&lt;p&gt;GitLab team advises developers to start the registry mirror&lt;sup id=&quot;fnref:4&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; that would avoid reaching pull rate limits. Mirror would cache the pulls and not turn to Docker Hub when the cache is there.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://www.docker.com/blog/scaling-docker-to-serve-millions-more-developers-network-egress/&quot; rel=&quot;nofollow external&quot;&gt;Scaling Docker to Serve Millions More Developers: Network Egress&lt;/a&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://circleci.com/docs/2.0/private-images/&quot; rel=&quot;nofollow external&quot;&gt;Using Docker Authenticated Pulls&lt;/a&gt; &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://circleci.com/docs/2.0/circleci-images/&quot; rel=&quot;nofollow external&quot;&gt;Pre-Built CircleCI Docker Images&lt;/a&gt; &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:4&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://about.gitlab.com/blog/2020/10/30/mitigating-the-impact-of-docker-hub-pull-requests-limits/#start-the-registry-mirror&quot; rel=&quot;nofollow external&quot;&gt;Caching Docker images to reduce the number of calls to DockerHub from your CI/CD infrastructure&lt;/a&gt; &lt;a href=&quot;#fnref:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Tue, 20 Oct 2020 00:00:00 +0000</pubDate>
        <link>https://tekkie.dev/devops/docker/devops-disruption-by-docker-hub-changes</link>
        <guid isPermaLink="true">https://tekkie.dev/devops/docker/devops-disruption-by-docker-hub-changes</guid>
        
        <category>docker</category>
        
        <category>devops</category>
        
        
        <category>devops</category>
        
        <category>docker</category>
        
      </item>
    
      <item>
        <title>First major CDN launches its data centre in Estonia</title>
        <description>&lt;p&gt;Big news from Cloudflare: it is expanding its CDN network to Baltics.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Bringing world-class web performance&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; to Baltic region, Cloudflare announced&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; its data centres in Tallinn (Estonia), Riga (Latvia), and Vilnius (Lithuania) today:&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Riga, Tallinn and Vilnius: Launching three new European Cloudflare data centers - &lt;a href=&quot;https://twitter.com/hashtag/MarchOfCloudflare?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#MarchOfCloudflare&lt;/a&gt; - &lt;a href=&quot;https://t.co/LX6Ga1tOvR&quot;&gt;https://t.co/LX6Ga1tOvR&lt;/a&gt; &lt;a href=&quot;https://t.co/cO4DHnHoJA&quot;&gt;pic.twitter.com/cO4DHnHoJA&lt;/a&gt;&lt;/p&gt;&amp;mdash; Cloudflare (@Cloudflare) &lt;a href=&quot;https://twitter.com/Cloudflare/status/979363166420439043?ref_src=twsrc%5Etfw&quot;&gt;March 29, 2018&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;p&gt;BALT-IX, LIXP, RTIX and Tallinn Internet Exchange (TLLIX)&lt;sup id=&quot;fnref:3&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; will all be used to interconnect with local networks.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;/devops/jekyll-http-response-handling-with-cloudflare-workers&quot;&gt;Jekyll HTTP response handling with Cloudflare Workers&lt;/a&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://blog.cloudflare.com/riga-tallinn-vilnius/?utm_medium=email&amp;amp;utm_source=blog&amp;amp;utm_campaign=rss-feed&quot;&gt;Riga, Tallinn and Vilnius: Launching three new European Cloudflare data centers&lt;/a&gt; &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;/telecom/linxtelecom-estonia-reports-52-percents-increase-in-sales&quot;&gt;Linxtelecom Estonia reports 52% increase in sales&lt;/a&gt; (2008) &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Thu, 29 Mar 2018 21:03:07 +0000</pubDate>
        <link>https://tekkie.dev/industry/first-major-cdn-launches-data-centre-in-estonia</link>
        <guid isPermaLink="true">https://tekkie.dev/industry/first-major-cdn-launches-data-centre-in-estonia</guid>
        
        <category>estonia</category>
        
        <category>cloudflare</category>
        
        <category>cloudflare workers</category>
        
        <category>TLLIX</category>
        
        <category>CDN</category>
        
        
        <category>industry</category>
        
      </item>
    
      <item>
        <title>Jekyll HTTP response handling with Cloudflare Workers</title>
        <description>&lt;p&gt;Jekyll is great at speed, which is why it’s used for GitHub Pages, but it’s not as flexible to maintain.&lt;!--more--&gt;&lt;/p&gt;

&lt;h2 id=&quot;hacked-wordpress&quot;&gt;Hacked Wordpress&lt;/h2&gt;

&lt;p&gt;Some years ago this very blog got hacked. It was on Wordpress. Code was injected to all PHP and HTML headers, hundreds of spam posts were created. These posts made it to sitemap and got indexed on Google. The blog was dead. Epic failure.&lt;/p&gt;

&lt;h2 id=&quot;introducing-devops&quot;&gt;Introducing DevOps&lt;/h2&gt;

&lt;p&gt;After some try ‘n cry development the issue got fixed by a migration from Wordpress to Jekyll. The blog entered DevOps era with automatic publishing on GitHub Pages.&lt;/p&gt;

&lt;p&gt;The remaining issue was the indexed spam content on Google.&lt;/p&gt;

&lt;h2 id=&quot;configuring-jekyll&quot;&gt;Configuring Jekyll&lt;/h2&gt;

&lt;p&gt;GitHub Pages ships with a sensible set of Jekyll plugins&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. One such example is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jekyll-redirect-from&lt;/code&gt;&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, that can be used to resolve Google’s 404 Crawl Errors by redirecting content.&lt;/p&gt;

&lt;p&gt;Even so, forwarding 404s raised by missing spam content does not make sense. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;410 Gone&lt;/code&gt; would be a more appropriate response, but there’s no obvious way in Jekyll.&lt;/p&gt;

&lt;h2 id=&quot;enter-cloudflare&quot;&gt;Enter Cloudflare&lt;/h2&gt;

&lt;p&gt;Cloudflare is great. Its free option can be used with GitHub Pages for &lt;abbr title=&quot;Transport Layer Security, a cryptographic protocol for secure Internet communication&quot;&gt;TLS&lt;/abbr&gt; termination, caching, page rules. The latter can do redirects, but not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;410&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;cloudflare-workers&quot;&gt;Cloudflare Workers&lt;/h3&gt;

&lt;p&gt;Cloudflare Workers&lt;sup id=&quot;fnref:3&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; is a relatively new feature that allows running code at the edge. It’s running V8 for JavaScript instead of the standard &lt;abbr title=&quot;Varnish Configuration Language, a domain-specific language used for configuring the Varnish&quot;&gt;VCL&lt;/abbr&gt;. Anyone with some basic JavaScript knowledge can create a custom response condition.&lt;/p&gt;

&lt;h3 id=&quot;javascript-for-410-response&quot;&gt;JavaScript for 410 response&lt;/h3&gt;

&lt;p&gt;Since all the spam URLs had a common pattern, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/eoti4/&amp;lt;dashed keywords&amp;gt;&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/nhuenh/&amp;lt;dashed keywords&amp;gt;&lt;/code&gt;, it took just 17 lines of JavaScript on Cloudflare Workers Editor to respond with 410 on all the spammy endpoints:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;respondWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetchAndLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetchAndLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\/(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;eoti4|nhuenh|nhueto|nhuegp|wish9y|nhsbsb|nhuekz&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Page is gone.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;410&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;statusText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Gone&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://help.github.com/articles/configuring-jekyll-plugins/#default-plugins&quot;&gt;Default Jekyll plugins on GitHub Pages&lt;/a&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://github.com/jekyll/jekyll-redirect-from&quot;&gt;jekyll-redirect-from&lt;/a&gt; - Seamlessly specify multiple redirections URLs for your pages and posts &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://www.cloudflare.com/products/cloudflare-workers/&quot;&gt;Cloudflare Workers&lt;/a&gt; - Run code at the edge, deliver powerful web extensibility &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Thu, 29 Mar 2018 00:00:00 +0000</pubDate>
        <link>https://tekkie.dev/devops/jekyll-http-response-handling-with-cloudflare-workers</link>
        <guid isPermaLink="true">https://tekkie.dev/devops/jekyll-http-response-handling-with-cloudflare-workers</guid>
        
        <category>jekyll</category>
        
        <category>devops</category>
        
        <category>cloudflare workers</category>
        
        
        <category>devops</category>
        
      </item>
    
      <item>
        <title>Stable Docker image to run JIRA</title>
        <description>&lt;p&gt;JIRA is one of the best Agile issue tracking tools out there, but choosing the right Docker image to run it is tricky.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Docker Hub lists more than 1 000 JIRA Docker images. Fortunately only a few have considerable number of stars helping to narrow it down to two:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://hub.docker.com/r/cptactionhank/atlassian-jira-software/&quot; rel=&quot;nofollow external&quot;&gt;cptactionhank/atlassian-jira-software&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://hub.docker.com/r/blacklabelops/jira/&quot; rel=&quot;nofollow external&quot;&gt;blacklabelops/jira&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When running JIRA behind HAProxy that terminates SSL, e.g. @LetsEncrypt, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cptactionhank/atlassian-jira-software&lt;/code&gt; turned out to be painful. Since JIRA does a lot of AJAX, considerable number of XHR requests broke over SSL termination rendering this Atlassian JIRA Docker image unusable.&lt;/p&gt;

&lt;p&gt;In comparison, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;blacklabelops/jira&lt;/code&gt; ran out of the box with the following Docker Compose configuration:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;blacklabelops/jira:7.5.0&apos;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;hostname&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;jira&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;host&amp;gt;&apos;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;restart&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;always&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;8080:8080&apos;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;links&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;db&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;./jira:/var/atlassian/jira&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;JIRA_DATABASE_URL&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mysql://&amp;lt;database&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;user&amp;gt;@db/&amp;lt;database&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;name&amp;gt;&apos;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;JIRA_DB_PASSWORD&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;database&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;password&amp;gt;&apos;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;JIRA_PROXY_NAME&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;jira&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;host&amp;gt;&apos;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;JIRA_PROXY_PORT&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;443&apos;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;JIRA_PROXY_SCHEME&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https&apos;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;DOCKER_WAIT_HOST&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;db&apos;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;DOCKER_WAIT_PORT&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3306&apos;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;json-file&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;max-size&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10m&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mariadb:10.1&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;database&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;name&amp;gt;&apos;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MYSQL_USER&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;database&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;user&amp;gt;&apos;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MYSQL_PASSWORD&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;database&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;password&amp;gt;&apos;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;database&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;password&amp;gt;&apos;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;./data:/var/lib/mysql&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;json-file&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;max-size&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10m&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;restart&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;always&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mysqld --character-set-server=utf8 --collation-server=utf8_bin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 15 Mar 2018 00:00:00 +0000</pubDate>
        <link>https://tekkie.dev/devops/stable-docker-image-to-run-jira</link>
        <guid isPermaLink="true">https://tekkie.dev/devops/stable-docker-image-to-run-jira</guid>
        
        <category>jira</category>
        
        <category>devops</category>
        
        <category>docker</category>
        
        
        <category>devops</category>
        
      </item>
    
      <item>
        <title>Facebook detox experiment</title>
        <description>&lt;p&gt;Facebook is toxic. It farms people for data and feeds off reactionary content igniting polarised discussions. It delivers very little value for the time spent.&lt;!--more--&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-background&quot;&gt;The background&lt;/h2&gt;

&lt;h3 id=&quot;fast-everything&quot;&gt;Fast everything&lt;/h3&gt;

&lt;p&gt;H&amp;amp;M does fast fashion, McDonald’s does fast food and IKEA does fast furniture. &lt;strong&gt;Facebook is about fast friendships&lt;/strong&gt;. Unfriending works the same way as does throwing away a tee shirt that only cost 5€ to buy. Quantity over quality.&lt;/p&gt;

&lt;p&gt;One of Hollywood’s greatest, Denzel Washington, put it nicely:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We all want to be liked, but now we all want to be liked by 16 million&lt;/p&gt;
&lt;/blockquote&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube-nocookie.com/embed/0bj3j8WHh5k?rel=0&quot; title=&quot;Denzel Washington on Social Media&quot; allow=&quot;encrypted-media&quot; allowfullscreen=&quot;&quot; class=&quot;video video--padded&quot;&gt;&lt;/iframe&gt;

&lt;h3 id=&quot;privacy&quot;&gt;Privacy&lt;/h3&gt;

&lt;p&gt;Facebook’s success builds on privacy violations. You’re in a private chat telling your friend about a football team you’ve never mentioned before and 2 minutes later you have a fan gear ad of the team in your Newsfeed. All your data, however private, is digested for advertising purposes.&lt;/p&gt;

&lt;p&gt;In 2014 I joined @maxschrems’ class action against Facebook exactly because of it. The lawsuit has gone all the way up to Court of Justice of the European Union, awaiting verdict.&lt;/p&gt;

&lt;h3 id=&quot;negativity&quot;&gt;Negativity&lt;/h3&gt;

&lt;p&gt;Facebook provides an excellent platform for flat earthers, anti-vaxxers, trumpists. Hate speech and alternative facts distribute with little control. The more people like this type of content, the more likely it is to appear on their Newsfeeds. Facebook’s algorithm takes care of that for obvious reasons: getting people hooked for higher advertising conversions.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fast friends&lt;/em&gt; are the usual suspects of corrupt Newsfeed and whilst confronting them is not mandatory, it’s sad to witness. Not merely a presumption, Facebook use has been directly linked to depressioni.&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-experiment&quot;&gt;The experiment&lt;/h2&gt;

&lt;p&gt;Since I removed Facebook from all my mobile devices a year ago already, next steps should go further:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Deactivate Facebook for a quarter as of Jan 15th&lt;/li&gt;
  &lt;li&gt;Monitor
    &lt;ul&gt;
      &lt;li&gt;overall productivity&lt;/li&gt;
      &lt;li&gt;spare time for activities like sports, hobbies, contributions to Open Source Software&lt;/li&gt;
      &lt;li&gt;communication with friends&lt;/li&gt;
      &lt;li&gt;mood&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Addressing all friends until further notice: text or call. Our keystrokes are limited.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://www.forbes.com/sites/amitchowdhry/2016/04/30/study-links-heavy-facebook-and-social-media-usage-to-depression/&quot; rel=&quot;nofollow external&quot;&gt;Study Links Heavy Facebook And Social Media Usage To Depression&lt;/a&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Thu, 11 Jan 2018 00:00:00 +0000</pubDate>
        <link>https://tekkie.dev/privacy/facebook-detox-experiment</link>
        <guid isPermaLink="true">https://tekkie.dev/privacy/facebook-detox-experiment</guid>
        
        <category>socialmedia</category>
        
        <category>facebook</category>
        
        <category>privacy</category>
        
        <category>productivity</category>
        
        <category>detox</category>
        
        
        <category>privacy</category>
        
      </item>
    
      <item>
        <title>How to run AngularJS end-to-end tests on GitLab CI</title>
        <description>&lt;p&gt;AngularJS-based applications are meant to be developed in test-driven manner, but how to run e2e tests in browser on GitLab CI?&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;End-to-end tests for AngularJS run TypeScript specs on Protractor in real browser as if user would interact with software.&lt;/p&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The problem&lt;/h2&gt;

&lt;p&gt;How to launch an actual browser on Continuous Integration platform with just command line at our disposal? There is no obvious out-of-the-box solution for CI/CD pipelines.&lt;/p&gt;

&lt;h2 id=&quot;the-plan&quot;&gt;The plan&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Run Xvfb (X virtual framebuffer)&lt;/li&gt;
  &lt;li&gt;Launch all AngularJS tests on Xvfb display with headless Chrome (Selenium testing)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;the-solution&quot;&gt;The solution&lt;/h2&gt;

&lt;h3 id=&quot;protractor-configuration&quot;&gt;Protractor configuration&lt;/h3&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// protractor.conf.js&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SpecReporter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;jasmine-spec-reporter&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;allScriptsTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;11000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;specs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// spec paths&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./e2e/**/*.e2e-spec.ts&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;capabilities&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;browserName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;chrome&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;chromeOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;--headless&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;--no-sandbox&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;directConnect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;baseUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;http://localhost:4200/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;framework&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;jasmine&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;jasmineNodeOpts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;showColors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;defaultTimeoutInterval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;30000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;beforeLaunch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;ts-node&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;e2e/tsconfig.e2e.json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;onPrepare&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;jasmine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getEnv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addReporter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SpecReporter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;displayStacktrace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}));&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;gitlab-ci-configuration&quot;&gt;GitLab CI configuration&lt;/h3&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# .gitlab-ci.yml&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;node:8&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;stages&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;test&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;test&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;before_script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Add Google Chrome to aptitude&apos;s (package manager) sources&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;echo &quot;deb http://dl.google.com/linux/chrome/deb/ stable main&quot; | tee -a /etc/apt/sources.list&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Fetch Chrome&apos;s PGP keys for secure installation&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Update aptitude&apos;s package sources&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apt-get -qq update -y&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Install latest Chrome stable, Xvfb packages&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apt-get -qq install -y google-chrome-stable xvfb gtk2-engines-pixbuf xfonts-cyrillic xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable imagemagick x11-apps default-jre&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Launch Xvfb&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Xvfb :0 -ac -screen 0 1024x768x24 &amp;amp;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Export display for Chrome&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;export DISPLAY=:99&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Install AngularJS CLI exclusively&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Add --unsafe-perm to resolve problems with node-gyp infinite loop on Docker&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;npm install --silent --unsafe-perm -g @angular/cli@1.1.2&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Install remaining project dependencies&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;npm install --silent&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Download Selenium server JAR, drivers for Chrome&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;node ./node_modules/.bin/webdriver-manager update&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ng test --single-run --progress &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ng e2e --progress &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;before_script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;npm install --silent --unsafe-perm -g @angular/cli@1.1.2&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;npm install --silent&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ng build --prod --progress &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;artifacts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dist/&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;only&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;master&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Wed, 13 Dec 2017 00:00:00 +0000</pubDate>
        <link>https://tekkie.dev/devops/angular-e2e-tests-on-gitlab-ci</link>
        <guid isPermaLink="true">https://tekkie.dev/devops/angular-e2e-tests-on-gitlab-ci</guid>
        
        <category>gitlab ci angular</category>
        
        <category>gitlab ci protractor</category>
        
        <category>angular ci</category>
        
        <category>ng e2e</category>
        
        <category>headless chrome</category>
        
        
        <category>devops</category>
        
      </item>
    
      <item>
        <title>Open Source should encourage contributors</title>
        <description>&lt;p&gt;As a maintainer, &lt;strong&gt;you can not afford to lose contributors&lt;/strong&gt;, esp. if your day job is not &lt;abbr title=&quot;Free and Open Source Software&quot;&gt;FOSS&lt;/abbr&gt; and you’re not Linus Torvalds.&lt;!--more--&gt;&lt;/p&gt;

&lt;h2 id=&quot;open-source-documentation&quot;&gt;Open Source documentation&lt;/h2&gt;

&lt;p&gt;There’s a serious problem with documentation in Open Source. 60% of contributors rarely or never contribute documentation&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. It kills adoption. Negative interactions over dismissive responses, conflicts, unexplained rejections of Pull Requests etc. amount to around 50% of experiences. It kills contributions.&lt;/p&gt;

&lt;h2 id=&quot;communication-quality-of-open-source-maintenance&quot;&gt;Communication quality of Open Source maintenance&lt;/h2&gt;

&lt;p&gt;Every project has and will receive flawed Pull Requests. Things that can not be merged over its quality, missing specs or bloat. All fair reasons. What matters is how that Code Review reveals these shortcomings. &lt;strong&gt;Interaction quality matters&lt;/strong&gt;. Instead of closing the Pull Request right off, you can change its course. Explain the shortcomings politely, propose, and, in case of something being overlooked, lead a contributor to work on documentation instead. Explaining the situation may take maintainer 5 minutes more than the actual implementation would, but it will engage the contributor. People will see that Pull Request merged and continue on a positive note.&lt;/p&gt;

&lt;p&gt;To illustrate the scenario, &lt;a href=&quot;https://github.com/ain/smartbanner.js&quot;&gt;smartbanner.js&lt;/a&gt; received &lt;a href=&quot;https://github.com/ain/smartbanner.js/pull/51&quot;&gt;#51&lt;/a&gt; recently where the contributor was not aware that his implementation duplicates already existing functionality. The easiest would have been to close the &lt;abbr title=&quot;Pull Request&quot;&gt;PR&lt;/abbr&gt; with one-liner tip that would have explained it. Instead, I requested changes in Code Review, explaining the situation and referring to the need of documentation update. That PR was merged today with a valuable documentation update.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;http://opensourcesurvey.org/2017&quot;&gt;Open Source Survey 2017&lt;/a&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Wed, 07 Jun 2017 00:00:00 +0000</pubDate>
        <link>https://tekkie.dev/opensource/open-source-should-encourage</link>
        <guid isPermaLink="true">https://tekkie.dev/opensource/open-source-should-encourage</guid>
        
        <category>opensource</category>
        
        <category>community</category>
        
        <category>software</category>
        
        
        <category>opensource</category>
        
      </item>
    
  </channel>
</rss>
