<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[/admcpr]]></title><description><![CDATA[For every complex problem there is an answer that is clear, simple, and wrong.]]></description><link>https://admcpr.com/</link><image><url>https://admcpr.com/favicon.png</url><title>/admcpr</title><link>https://admcpr.com/</link></image><generator>Ghost 5.59</generator><lastBuildDate>Fri, 23 Feb 2024 06:46:49 GMT</lastBuildDate><atom:link href="https://admcpr.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[How to fix 'Please resolve before detaching' on a surface book]]></title><description><![CDATA[<p>If you use Brave as your browser and you&apos;re also using a Surface Book (or some other Windows detachable with multiple GPUs) then this message is going to spoil your detaching fun:</p><blockquote><strong>Please resolve before detaching</strong><br>The following may need to be closed in order to detach.<br>Any</blockquote>]]></description><link>https://admcpr.com/how-to-fix-please-resolve-before-detaching-on-a-surface-book/</link><guid isPermaLink="false">64d51a7bb72ae7949570cc90</guid><category><![CDATA[Windows]]></category><dc:creator><![CDATA[Adam Cooper]]></dc:creator><pubDate>Sun, 13 Aug 2023 19:56:53 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1584363835508-fd457702a7ed?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fHN1cmZhY2UlMjBib29rfGVufDB8fHx8MTY5NDU4Nzg2M3ww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1584363835508-fd457702a7ed?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fHN1cmZhY2UlMjBib29rfGVufDB8fHx8MTY5NDU4Nzg2M3ww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="How to fix &apos;Please resolve before detaching&apos; on a surface book"><p>If you use Brave as your browser and you&apos;re also using a Surface Book (or some other Windows detachable with multiple GPUs) then this message is going to spoil your detaching fun:</p><blockquote><strong>Please resolve before detaching</strong><br>The following may need to be closed in order to detach.<br>Any unsaved changes will be lost.</blockquote><figure class="kg-card kg-image-card"><img src="https://admcpr.com/content/images/2023/08/image.png" class="kg-image" alt="How to fix &apos;Please resolve before detaching&apos; on a surface book" loading="lazy" width="726" height="378" srcset="https://admcpr.com/content/images/size/w600/2023/08/image.png 600w, https://admcpr.com/content/images/2023/08/image.png 726w" sizes="(min-width: 720px) 720px"></figure><h3 id="so-why-does-it-happen">So why does it happen?</h3><p>By default, Brave uses the primary GPU which is the one in the base and as it&apos;s dependent on that while running then Windows won&apos;t let the screen be detached from the base.</p><h3 id="and-how-do-i-fix-it">And how do I fix it?</h3><p>In short, tell Brave to use the other (power saving) GPU instead.</p><p>In a bit more detail:</p><p>Open <strong>Settings &gt; Display &gt; Graphics</strong>. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2023/08/image-1.png" class="kg-image" alt="How to fix &apos;Please resolve before detaching&apos; on a surface book" loading="lazy" width="1248" height="758" srcset="https://admcpr.com/content/images/size/w600/2023/08/image-1.png 600w, https://admcpr.com/content/images/size/w1000/2023/08/image-1.png 1000w, https://admcpr.com/content/images/2023/08/image-1.png 1248w" sizes="(min-width: 720px) 720px"><figcaption>Settings &gt; Display &gt; Graphics</figcaption></figure><p>From the <strong>Add an App</strong> dropdown select <strong>Desktop app</strong> and click <strong>Browse</strong>, navigate to the <code>brave.exe</code> executable, and select it. By default, it&apos;ll be at <code>C:\Program Files\BraveSoftware\Brave-Browser\Application\brave.exe</code>. Now click <strong>Options</strong> on the Brave Browser card that&apos;s added to your options.</p><figure class="kg-card kg-image-card"><img src="https://admcpr.com/content/images/2023/08/image-2.png" class="kg-image" alt="How to fix &apos;Please resolve before detaching&apos; on a surface book" loading="lazy" width="1016" height="622" srcset="https://admcpr.com/content/images/size/w600/2023/08/image-2.png 600w, https://admcpr.com/content/images/size/w1000/2023/08/image-2.png 1000w, https://admcpr.com/content/images/2023/08/image-2.png 1016w" sizes="(min-width: 720px) 720px"></figure><p>Finally select <strong>Power saving</strong>, save everything and restart Brave.</p><figure class="kg-card kg-image-card"><img src="https://admcpr.com/content/images/2023/08/image-4.png" class="kg-image" alt="How to fix &apos;Please resolve before detaching&apos; on a surface book" loading="lazy" width="886" height="876" srcset="https://admcpr.com/content/images/size/w600/2023/08/image-4.png 600w, https://admcpr.com/content/images/2023/08/image-4.png 886w" sizes="(min-width: 720px) 720px"></figure><p> And now you can detach your screen without closing your browser and as an added bonus your laptop should run cooler and the battery will last for longer too!</p>]]></content:encoded></item><item><title><![CDATA[Postgres Full Text Search is better than ... (Part 2)]]></title><description><![CDATA[In which I show how ElasticSearch, Solr or another search specific database is probably unnecessary for a lot of PostgreSQL users ... Part 2.]]></description><link>https://admcpr.com/postgres-full-text-search-is-better-than-part-2/</link><guid isPermaLink="false">642ae33768e030ea5e93966c</guid><category><![CDATA[SQL]]></category><category><![CDATA[PostgreSQL]]></category><dc:creator><![CDATA[Adam Cooper]]></dc:creator><pubDate>Fri, 21 Apr 2023 14:05:55 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1496449903678-68ddcb189a24?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDIzfHxzZWFyY2h8ZW58MHx8fHwxNjgwNTMwNzE1&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1496449903678-68ddcb189a24?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDIzfHxzZWFyY2h8ZW58MHx8fHwxNjgwNTMwNzE1&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Postgres Full Text Search is better than ... (Part 2)"><p>Part 2 of a 2-part post on the FTS features available in Postgres and how you might use them to build a simple search API. If you want to follow along head over to the GitHub repo, grab the source data and spin up the included docker config, we&apos;ll be searching a database of 50,000 movies. Check out <a href="https://admcpr.com/postgres-full-text-search-is-better-than-part1/">part 1</a> and the accompanying <a href="https://github.com/admcpr/postgres-full-text-search-is-better-than?ref=admcpr.com">GitHub repository.</a></p><h2 id="searching-multiple-columns">Searching multiple columns</h2><p>Now what if we want to search across multiple columns, for example our database has an <code>original_title</code> and a <code>title</code> column for each movie. We can concatenate each column <code>tsvector</code> so we have one <code>tsvector</code> to search.</p><pre><code class="language-SQL">select to_tsvector(title) || &apos; &apos; ||  to_tsvector(original_title)
from movies</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2023/04/image-13.png" class="kg-image" alt="Postgres Full Text Search is better than ... (Part 2)" loading="lazy" width="546" height="188"><figcaption>4803 results: 85msec.</figcaption></figure><p>But what if I want to prioritise search results in certain columns, for example I want matches in <code>title</code> to be ranked higher than <code>original_title</code>. &#xA0;Of course, Postgres has this covered with the <code>setweight</code> function which we can use to weight the results of any <code>tsvector</code> from <code>A</code>, the highest weighted, to <code>D</code>, the lowest.</p><pre><code class="language-SQL">select setweight(to_tsvector(title), &apos;A&apos;) || &apos; &apos; || setweight(to_tsvector(original_title), &apos;B&apos;)
from movies</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2023/04/image-14.png" class="kg-image" alt="Postgres Full Text Search is better than ... (Part 2)" loading="lazy" width="660" height="187" srcset="https://admcpr.com/content/images/size/w600/2023/04/image-14.png 600w, https://admcpr.com/content/images/2023/04/image-14.png 660w"><figcaption>4803 results: 89 msec.</figcaption></figure><h2 id="make-querying-easier">Make querying easier</h2><p>Now we can, for example, search for the movie Hero by its original title.</p><pre><code class="language-SQL">select title, original_title, 
	ts_rank(to_tsvector(title) || &apos; &apos; ||  to_tsvector(original_title), websearch_to_tsquery(&apos;&#x82F1;&#x96C4;&apos;)) as rank
from movies
where to_tsvector(title) || &apos; &apos; ||  to_tsvector(original_title) 
	@@ websearch_to_tsquery(&apos;&#x82F1;&#x96C4;&apos;)
order by rank desc </code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2023/04/image-15.png" class="kg-image" alt="Postgres Full Text Search is better than ... (Part 2)" loading="lazy" width="388" height="70"><figcaption>1 result: 108 msec.</figcaption></figure><p>This is pretty powerful, but our SQL is starting to get a bit cumbersome, and it would be nicer, and more performant, if we didn&apos;t have to build our vectors for every query. We could create a <code>tsvector</code> column on the table and store the weighted vectors and then use triggers to keep that column updated on any change to the table data. Yuck... &#x1F922;</p><p>So instead let&apos;s take advantage of another awesome Postgres feature <a href="https://www.postgresql.org/docs/12/ddl-generated-columns.html?ref=admcpr.com">Generated Columns</a>. </p><blockquote>A generated column is a special column that is always computed from other columns. Thus, it is for columns what a view is for tables.</blockquote><pre><code class="language-SQL">alter table movies 
add title_search tsvector 
generated always as	(
	setweight(to_tsvector(&apos;simple&apos;, coalesce(title, &apos;&apos;)), &apos;A&apos;) || &apos; &apos; || 
	setweight(to_tsvector(&apos;simple&apos;, coalesce(original_title, &apos;&apos;)), &apos;B&apos;) :: tsvector
) stored; </code></pre><p>We&apos;ve changed the way we call <code>to_tsvector</code> slightly here because our generated column must be immutable so we use <code>coalesce</code> to make sure any <code>null</code> values will be represented as an empty string, and we also specify that we&apos;re using the <code>&apos;simple&apos;</code> text search configuration (more on that in just a moment). </p><p>For now, let&apos;s just enjoy how quick and easy it is to query our weighted vectors. </p><pre><code class="language-SQL">select title, original_title, ts_rank(title_search, websearch_to_tsquery(&apos;hero&apos;)) as rank
from movies
where title_search @@ websearch_to_tsquery(&apos;hero&apos;)
order by rank desc</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2023/04/image-16.png" class="kg-image" alt="Postgres Full Text Search is better than ... (Part 2)" loading="lazy" width="456" height="151"><figcaption>5 results: 81 msecs.</figcaption></figure><h2 id="a-brief-discussion-of-text-search-configuration">A brief discussion of Text Search Configuration</h2><blockquote><a href="https://www.postgresql.org/docs/current/textsearch-configuration.html?ref=admcpr.com">A text search configuration specifies all options necessary to transform a document into a <code>tsvector</code></a></blockquote><p>We can see a list of the available Text Search Configurations (TSC) for our Postgres instance by querying <code>pg_ts_config</code>.</p><pre><code class="language-SQL">SELECT * FROM pg_ts_config</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2023/04/image-19.png" class="kg-image" alt="Postgres Full Text Search is better than ... (Part 2)" loading="lazy" width="608" height="221" srcset="https://admcpr.com/content/images/size/w600/2023/04/image-19.png 600w, https://admcpr.com/content/images/2023/04/image-19.png 608w"><figcaption>29 results: 83 msecs.</figcaption></figure><p>The TSCs available by defaults are each focused on a language and are configured to use a language specific <a href="https://www.postgresql.org/docs/current/textsearch-dictionaries.html?ref=admcpr.com">dictionary</a>. So far, we have always used the <a href="https://www.postgresql.org/docs/15/textsearch-dictionaries.html?ref=admcpr.com#TEXTSEARCH-SIMPLE-DICTIONARY">simple</a> TSC when calling <code>to_tsvector</code> and <code>*to_tsquery</code>. This TSC &apos;simply&apos; converts the input tokens to lower case and discards common stop words.</p><pre><code class="language-SQL">select to_tsvector(&apos;simple&apos;, &apos;To fish the fishes a fishy fisher fished&apos;)</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2023/04/image-17.png" class="kg-image" alt="Postgres Full Text Search is better than ... (Part 2)" loading="lazy" width="476" height="75"><figcaption>1 result: 75 msecs.</figcaption></figure><p>If we use a language specific TSC (<code>english</code>) instead then many more, English, stop words are discarded and each word is normalized so that, for example, the plural of <em>fishes</em> would be normalized to <em>fish</em>. </p><pre><code>select to_tsvector(&apos;english&apos;, &apos;To fish the fishes a fishy fisher fished&apos;)</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2023/04/image-18.png" class="kg-image" alt="Postgres Full Text Search is better than ... (Part 2)" loading="lazy" width="269" height="84"><figcaption>1 result: 77 msecs.</figcaption></figure><p>This has many benefits, first it saves space because fewer normalized values (lexemes) are stored. Second by normalizing words to lexemes based on the dictionary we are able to take advantage of stemming so that, for example, a search for fishes would match with fish, fishes, fished. Thirdly it means that all of this can be done in a way that supports whichever language our documents may be stored and searched in. </p><p>You can customize TSCs or build your own to support synonyms, thesauruses and much more, I&apos;d highly recommend reading the <a href="https://www.postgresql.org/docs/current/textsearch-configuration.html?ref=admcpr.com">Text Search Configuration documentation</a> if you&apos;d like to learn more. &#xA0;</p><h2 id="will-it-index">Will it index?</h2><p>OK back to searching our movie database and at this point we need to start thinking about performance. Our database only contains a few thousand records, so our query timings are all measured in milliseconds. But what would happen if we were querying hundreds of thousands or millions of rows? Put simply, performance would suck because right now we&apos;re doing a sequential scan for every query which requires iterating through every row of the table. If we analyse our query, we can see a clear warning about this.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2023/04/image-20.png" class="kg-image" alt="Postgres Full Text Search is better than ... (Part 2)" loading="lazy" width="1452" height="234" srcset="https://admcpr.com/content/images/size/w600/2023/04/image-20.png 600w, https://admcpr.com/content/images/size/w1000/2023/04/image-20.png 1000w, https://admcpr.com/content/images/2023/04/image-20.png 1452w" sizes="(min-width: 720px) 720px"><figcaption>Seq Scan = :(</figcaption></figure><p>If only Postgres had some kind of special index designed for speeding up full text search that would be awesome, and of course it does ... <a href="https://www.postgresql.org/docs/15/gin.html?ref=admcpr.com">GIN indexes</a>. </p><pre><code class="language-SQL">create index idx_search on movies using GIN(title_search)</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2023/04/image-21.png" class="kg-image" alt="Postgres Full Text Search is better than ... (Part 2)" loading="lazy" width="1450" height="284" srcset="https://admcpr.com/content/images/size/w600/2023/04/image-21.png 600w, https://admcpr.com/content/images/size/w1000/2023/04/image-21.png 1000w, https://admcpr.com/content/images/2023/04/image-21.png 1450w" sizes="(min-width: 720px) 720px"><figcaption>Index Scan = :)</figcaption></figure><p>You can see the huge reduction in the <code>Timings</code> above, we&apos;ve gone from around 7.5 ms to less than 0.04 ms, and at this point we have a search query that can be fast enough for a real production environment. </p><h3 id="addendum">Addendum</h3><p>Just one more thing before we&apos;re done. Intuitively if I was to run a title search for &apos;rush&apos; I would expect the highest ranked result to be the movie with the title &apos;Rush&apos; but, by default, the <code>ts_rank</code> function doesn&apos;t consider document or query length. </p><pre><code class="language-SQL">select title, ts_rank(title_search, websearch_to_tsquery(&apos;rush&apos;)) as rank
from movies
where title_search @@ websearch_to_tsquery(&apos;rush&apos;)
order by rank desc</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2023/04/image-24.png" class="kg-image" alt="Postgres Full Text Search is better than ... (Part 2)" loading="lazy" width="289" height="158"><figcaption>6 results: 76 msecs.</figcaption></figure><p>If we pass a <a href="https://www.postgresql.org/docs/current/textsearch-controls.html?ref=admcpr.com#TEXTSEARCH-RANKING"><code>normalization</code> </a>option to <code>ts_rank</code> we can specify how the document&apos;s length should impact its rank and make our results more intuitive.</p><pre><code class="language-SQL">select title, ts_rank(title_search, websearch_to_tsquery(&apos;rush&apos;), 1) as rank
from movies
where title_search @@ websearch_to_tsquery(&apos;rush&apos;)
order by rank desc</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2023/04/image-23.png" class="kg-image" alt="Postgres Full Text Search is better than ... (Part 2)" loading="lazy" width="294" height="160"><figcaption>6 results: 75 msecs.</figcaption></figure><h3 id="the-end">-- The End</h3>]]></content:encoded></item><item><title><![CDATA[Postgres Full Text Search is better than ... (Part 1)]]></title><description><![CDATA[In which I show how ElasticSearch, Solr or another search specific database is probably unnecessary for a lot of PostgreSQL users. ]]></description><link>https://admcpr.com/postgres-full-text-search-is-better-than-part1/</link><guid isPermaLink="false">63cbf7b7778d3b0aa8f47845</guid><category><![CDATA[PostgreSQL]]></category><category><![CDATA[SQL]]></category><dc:creator><![CDATA[Adam Cooper]]></dc:creator><pubDate>Mon, 03 Apr 2023 15:23:46 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1496449903678-68ddcb189a24?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDIzfHxzZWFyY2h8ZW58MHx8fHwxNjgwNTMwNzE1&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1496449903678-68ddcb189a24?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDIzfHxzZWFyY2h8ZW58MHx8fHwxNjgwNTMwNzE1&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Postgres Full Text Search is better than ... (Part 1)"><p>Everyone needs Full Text Search (FTS) at some point and the most common way to get it is to push your data to Elastic/OpenSearch, Solr, or something else built on top of Lucene. It&apos;s a solution that works but adds extra complexity, cost, and consistency challenges. So, what if I told you that, if you&apos;re using PostgreSQL, powerful FTS is available to you out of the box?</p><h3 id="play-along">Play along</h3><p>If you want to follow along head over to the GitHub repo and spin up the docker file and included source data.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/admcpr/postgres-full-text-search-is-better-than?ref=admcpr.com"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - admcpr/postgres-full-text-search-is-better-than: A PostgreSQL database and all the data needed to follow along with the `Postgres Full Text Search is better than ...` blog posts</div><div class="kg-bookmark-description">A PostgreSQL database and all the data needed to follow along with the `Postgres Full Text Search is better than ...` blog posts - GitHub - admcpr/postgres-full-text-search-is-better-than: A Postgr&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt="Postgres Full Text Search is better than ... (Part 1)"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">admcpr</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://repository-images.githubusercontent.com/613326585/e9e0ec3f-e616-4799-a926-f1a702f7a781" alt="Postgres Full Text Search is better than ... (Part 1)"></div></a></figure><h3 id="postgres-text-search-the-wrong-way%EF%B8%8F">Postgres text search the wrong way&#xFE0F;</h3><p>We&apos;re going to build the SQL for a hypothetical API endpoint that searches movies. Let&apos;s start by trying to search for a well-known film by its title the wrong way&#x2122;&#xFE0F;. We can use <code>like</code> to do basic pattern matching so a simple solution might be to split the search input into individual words and wrap them with <code>%</code>.</p><pre><code class="language-SQL">select title from movies where title like &apos;%star%&apos; or title like &apos;%wars%&apos; </code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2023/04/image-1.png" class="kg-image" alt="Postgres Full Text Search is better than ... (Part 1)" loading="lazy" width="157" height="61"><figcaption>2 results: 61 msec.</figcaption></figure><p>Two results, but not Star Wars though because of course I&apos;ve forgotten that <code>like</code> is case sensitive. </p><pre><code class="language-SQL">select title from assets where title ilike &apos;%star%&apos; or title ilike &apos;%wars%&apos; </code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2023/04/image-2.png" class="kg-image" alt="Postgres Full Text Search is better than ... (Part 1)" loading="lazy" width="371" height="177"><figcaption>36 results: 76 msec.</figcaption></figure><p>Now I get 36 results and Star Wars is in there, but the basic pattern matching provided by <code>ilike</code> doesn&apos;t rank our results and is never going to help us with more complicated concepts like stemming. </p><h3 id="help-me-tsvector-you-are-my-only-hope">Help me tsvector you are my only hope</h3><p>So enough of what won&apos;t work let&apos;s look at some tools that will. We&apos;re going to start with <a href="https://www.postgresql.org/docs/current/datatype-textsearch.html?ref=admcpr.com#DATATYPE-TSVECTOR"><code>tsvector</code></a> which, to quote the Postgres documentation:</p><blockquote>A <code>tsvector</code> value is a sorted list of distinct <em><em>lexemes</em></em>, which are words that have been <em><em>normalized</em></em> to merge different variants of the same word.</blockquote><p>We can use the <a href="https://www.postgresql.org/docs/current/textsearch-controls.html?ref=admcpr.com#TEXTSEARCH-PARSING-DOCUMENTS"><code>to_tsvector</code></a> function to parse text and turn into a <code>tsvector</code> for us. </p><pre><code class="language-SQL">select to_tsvector(&apos;I am altering the deal. Pray I don&apos;&apos;t alter it any further!&apos;);</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2023/04/image-3.png" class="kg-image" alt="Postgres Full Text Search is better than ... (Part 1)" loading="lazy" width="412" height="36"><figcaption>1 result: 73 msec.</figcaption></figure><p>Now let&apos;s do that to some of our movie titles.</p><pre><code class="language-SQL">select title, to_tsvector(title) from movies limit 20;</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2023/04/image-4.png" class="kg-image" alt="Postgres Full Text Search is better than ... (Part 1)" loading="lazy" width="769" height="207" srcset="https://admcpr.com/content/images/size/w600/2023/04/image-4.png 600w, https://admcpr.com/content/images/2023/04/image-4.png 769w" sizes="(min-width: 720px) 720px"><figcaption>20 results: 60 msec.</figcaption></figure><p>Now we can use the <a href="https://www.postgresql.org/docs/current/textsearch-controls.html?ref=admcpr.com#TEXTSEARCH-PARSING-QUERIES"><code>to_tsquery</code></a> function to query the <code>tsvector</code> of the title instead of querying the title directly. Let&apos;s start simple. </p><pre><code class="language-SQL">select title 
from movies
where to_tsvector(title) @@ to_tsquery(&apos;Star&apos;)</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2023/04/image-5.png" class="kg-image" alt="Postgres Full Text Search is better than ... (Part 1)" loading="lazy" width="455" height="143"><figcaption>25 results: 133 msec.</figcaption></figure><p>It would be nice if we could order the results by how well they match our query so let&apos;s use the <code><a href="https://www.postgresql.org/docs/current/textsearch-controls.html?ref=admcpr.com#TEXTSEARCH-RANKING">ts_rank</a></code> function to return a value for that. </p><pre><code class="language-SQL">select title, ts_rank(to_tsvector(title), to_tsquery(&apos;Star&apos;)) as rank
from assets
where to_tsvector(title) @@ to_tsquery(&apos;star&apos;)</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2023/04/image-6.png" class="kg-image" alt="Postgres Full Text Search is better than ... (Part 1)" loading="lazy" width="491" height="146"><figcaption>25 results: 89 msec.</figcaption></figure><p>So, let&apos;s find Star Wars.</p><pre><code class="language-SQL">select title, ts_rank(to_tsvector(title), to_tsquery(&apos;star wars&apos;)) as rank
from movies
where to_tsvector(title) @@ to_tsquery(&apos;star wars&apos;) </code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2023/04/image-7.png" class="kg-image" alt="Postgres Full Text Search is better than ... (Part 1)" loading="lazy" width="464" height="61"><figcaption>ERROR: syntax error in tsquery</figcaption></figure><p>Oh, come on! It was all going so well but a restriction of <code>tsquery</code> is that it:</p><blockquote>must consist of single tokens separated by the <code>tsquery</code> operators <code>&amp;</code> (AND), <code>|</code> (OR), <code>!</code> (NOT), and <code>&lt;-&gt;</code> (FOLLOWED BY)</blockquote><p>In short, no spaces allowed. We could fix this simply by changing <code>&apos;star wars&apos;</code> to <code>&apos;star&lt;-&gt;wars&apos;</code> but then if we were, for example, building a search API endpoint we&apos;d need to parse the input, split and rejoin it. Instead let&apos;s just use <code>plainto_tsquery</code> which will generate a <code>tsquery</code> from plain text and now we can find Star Wars &#x1F389;</p><pre><code class="language-SQL">select title, ts_rank(to_tsvector(title), plainto_tsquery(&apos;star wars&apos;)) as rank
from movies
where to_tsvector(title) @@ plainto_tsquery(&apos;star wars&apos;)
order by rank desc</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2023/04/image-8.png" class="kg-image" alt="Postgres Full Text Search is better than ... (Part 1)" loading="lazy" width="561" height="112"><figcaption>5 results: 83 msec.</figcaption></figure><h3 id="more-powerful-search-syntax">More powerful search syntax</h3><p>As <code>tsquery</code> supports operators it would be nice to expose this functionality via our API so that a user could execute more complicated searches. We could either expect our users to learn the <code>tsquery</code> syntax or we could parse the incoming query and build a valid <code>tsquery</code> for them. Fortunately, though we don&apos;t have to do either of those because the <code>websearch_to_tsquery</code> function lets us support the following popular syntax for free. </p><ul><li><code>&quot;quoted text&quot;</code>: text inside quote marks will be converted to terms separated by <code>&lt;-&gt;</code> operators. Also, stop words are not simply discarded, but are accounted for by inserting <code>&lt;N&gt;</code> operators rather than <code>&lt;-&gt;</code> operators.</li><li><code>OR</code>: the word &#x201C;or&#x201D; will be converted to the <code>|</code> operator.</li><li><code>+</code>: a plus will be converted to the <code>&amp;</code> operator.</li><li><code>-</code>: a dash will be converted to the <code>!</code> operator.</li></ul><p>For example, <code>select websearch_to_tsquery(&apos;&quot;star wars&quot; -clone&apos;)</code> would give us a query of <code>&apos;star&apos;&lt;-&gt;&apos;war&apos;&amp;!clone&apos;</code> to search for results that include Star Wars but exclude Clone. Or if the user was only interested in Star Wars movies about Clones.</p><pre><code class="language-SQL">select title, ts_rank(to_tsvector(title), websearch_to_tsquery(&apos;&quot;star wars&quot; +clone&apos;)) as rank
from movies
where to_tsvector(title) @@ websearch_to_tsquery(&apos;&quot;star wars&quot; +clone&apos;)
order by rank desc </code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2023/04/image-9.png" class="kg-image" alt="Postgres Full Text Search is better than ... (Part 1)" loading="lazy" width="485" height="110"><figcaption>2 results: 88 msec.</figcaption></figure><h3 id="part-2">Part 2</h3><p>In the second, and final, part of this post we cover searching multiple columns, simpler queries, performance and more.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://admcpr.com/postgres-full-text-search-is-better-than-part-2/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Postgres Full Text Search is better than ... (Part 2)</div><div class="kg-bookmark-description">In which I show how ElasticSearch, Solr or another search specific database is probably unnecessary for a lot of PostgreSQL users ... Part 2.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://admcpr.com/content/images/size/w256h256/2019/09/portrait-half-icon.png" alt="Postgres Full Text Search is better than ... (Part 1)"><span class="kg-bookmark-author">/admcpr</span><span class="kg-bookmark-publisher">Adam Cooper</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://images.unsplash.com/photo-1496449903678-68ddcb189a24?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDIzfHxzZWFyY2h8ZW58MHx8fHwxNjgwNTMwNzE1&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Postgres Full Text Search is better than ... (Part 1)"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[How to Speak]]></title><description><![CDATA[I used to hate public speaking; I still do but I used to too (apologies to Mitch Hedberg).  But as I have spent less of my work time building and deploying software and more of it sharing why and how software is built and deployed, I've needed to get better at communicating ...]]></description><link>https://admcpr.com/how-to-speak/</link><guid isPermaLink="false">62b053e430937a33b620c0a5</guid><category><![CDATA[Links]]></category><dc:creator><![CDATA[Adam Cooper]]></dc:creator><pubDate>Wed, 15 Feb 2023 17:09:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1631220706319-657942774d02?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDkwfHxtaWNyb3Bob25lJTIwfGVufDB8fHx8MTY3NjQ4MDg3Mg&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1631220706319-657942774d02?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDkwfHxtaWNyb3Bob25lJTIwfGVufDB8fHx8MTY3NjQ4MDg3Mg&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="How to Speak"><p>I used to hate public speaking; I still do but I used to too (apologies to Mitch Hedberg). &#xA0;But as I have spent less of my work time building and deploying software and more of it sharing why and how software is built and deployed, I&apos;ve needed to get better at communicating to a room, or Zoom, full of people. </p><p>So, as it&apos;s something I must do I&apos;ve tried to learn how to be better at it which helps make me feel more comfortable and even sometimes enjoy it. The single most helpful resource I&apos;ve ever come across as I worked on that was this lecture given by <a href="https://en.wikipedia.org/wiki/Patrick_Winston?ref=admcpr.com">Patrick Winston</a> at MIT. It&apos;s entertaining, interesting and offers insights and tips on how to give a good presentation while giving a great presentation. A couple of choice snippets that I always try to remember:</p><ul><li>Tell people what they&apos;re going to know at the end of your talk that they didn&apos;t know at the beginning of your talk</li><li>Cycle over your subject to make sure you cover it clearly</li><li>Ask questions of your audience</li><li>Sum up information within your talk sometimes to help listeners re-engage</li><li>Questions are the worst way to end a talk</li><li>Don&apos;t start with a joke</li></ul><p>I considered trying to sum up the video in this blog post but honestly there&apos;s so much that&apos;s useful I would never do it justice and it&apos;s actually a very entertaining watch. Instead, I&apos;ll advise that you invest an hour of your life watching the video and in return, to quote from that video:</p><blockquote>By the end of the next 60 minutes, you&apos;ll have been exposed to a lot of ideas, some of which you&apos;ll incorporate into your own repertoire, and they will ensure that you get the maximum opportunity to have your ideas valued and accepted by the people you speak with. </blockquote><p>If you visit the MIT OpenCourseWare page which has the video available to watch or download plus transcripts and more. &#xA0;</p><figure class="kg-card kg-bookmark-card kg-card-hascaption"><a class="kg-bookmark-container" href="https://ocw.mit.edu/courses/res-tll-005-how-to-speak-january-iap-2018/pages/how-to-speak/?ref=admcpr.com"><div class="kg-bookmark-content"><div class="kg-bookmark-title">How to Speak | How to Speak | Supplemental Resources | MIT OpenCourseWare</div><div class="kg-bookmark-description">This page includes the full How to Speak video recorded in January 2018, along with related content and commentary</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://ocw.mit.edu/favicon.ico" alt="How to Speak"><span class="kg-bookmark-author">MIT OpenCourseWare</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://ocw.mit.edu/courses/res-tll-005-how-to-speak-january-iap-2018/28205c50444b2ad0368682a733f1a36c_RES-TLL-005IAP18.jpg" alt="How to Speak"></div></a><figcaption>MIT OpenCourseWare: How to Speak</figcaption></figure><p>Alternatively, you can watch the video on youtube.</p><figure class="kg-card kg-embed-card"><iframe width="200" height="113" src="https://www.youtube.com/embed/Unzc731iCUY?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><p></p><blockquote class="kg-blockquote-alt">Is a hippopotamus a hippopotamus... or a really cool opotamus? <br>- Mitch Hedberg</blockquote>]]></content:encoded></item><item><title><![CDATA[Compressing pdf files with ghostscript]]></title><description><![CDATA[How I used ghostscript to compress some ridiculously large pdf files for reading on my iPad.]]></description><link>https://admcpr.com/compress-a-pdf-using-ghostscript/</link><guid isPermaLink="false">6389d7dfb952b70e948c3d2d</guid><category><![CDATA[Windows]]></category><category><![CDATA[macOS]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Adam Cooper]]></dc:creator><pubDate>Fri, 02 Dec 2022 16:49:59 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1572852574060-fc1aaced7140?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDIwMHx8cGFja2luZ3xlbnwwfHx8fDE2Njk5OTk3NjY&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1572852574060-fc1aaced7140?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDIwMHx8cGFja2luZ3xlbnwwfHx8fDE2Njk5OTk3NjY&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Compressing pdf files with ghostscript"><p>Recently I bought a bundle of graphic novels written by the fantastic <a href="https://neilgaiman.com/?ref=admcpr.com">Neil Gaiman</a>, author of The Sandman, Coraline, many more.</p><p>Those novels were delivered as gigantic pdf files most of which were well over 1GB and that meant the old iPad I wanted to read them on was just not able to open them. Obviously, the ideal solution in this scenario is to buy a new iPad, yay! </p><p>But what if I could solve this problem without spending almost a thousand euros... perhaps I could just optimise these pdf files. Now I know that Acrobat can do that but then I need to spend hundreds of euros on an Adobe subscription and, honestly, I&apos;d rather buy a new iPad. I did try opening the pdf files in every browser and printing to pdf, but that just killed the pdf printer. </p><p>So, I did some searching for open-source pdf tools and I was reminded of <a href="https://www.ghostscript.com/?ref=admcpr.com">ghostscript</a> which is &apos;an interpreter for the PostScript&#xAE; language and PDF files&apos; that has been around for over 30 years. And not only is it great at converting pdf files, but it has a specific switch, <code>dPDFSETTINGS=/ebook</code>, that can be used to output PDF files optimised for an e-reader. Here&apos;s what I did.</p><h3 id="install-ghostscript">Install ghostscript</h3><figure class="kg-card kg-code-card"><pre><code class="language-Powershell">winget install ArtifexSoftware.GhostScript
</code></pre><figcaption>Windows</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-zsh">brew install ghostscript</code></pre><figcaption>macOS</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-bash">sudo apt install ghostscript</code></pre><figcaption>Ubuntu</figcaption></figure><h3 id="convert-the-pdf-files">Convert the PDF files</h3><figure class="kg-card kg-code-card"><pre><code class="language-Powershell">gswin64c.exe -sDEVICE=pdfwrite -dPDFSETTINGS=/ebook -dNOPAUSE -dQUIET -dBATCH -sOutputFile=&quot;output.pdf&quot; input.pdf</code></pre><figcaption>Windows</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-bash/zsh">ghostscript -sDEVICE=pdfwrite -dPDFSETTINGS=/ebook -dNOPAUSE -dQUIET -dBATCH -sOutputFile=&quot;output.pdf&quot; input.pdf</code></pre><figcaption>Ubuntu/macOS</figcaption></figure><p>And once the conversion process completed, it takes a while, I had gone from having a 1.3GB pdf that my iPad couldn&apos;t even open to a 68MB pdf that opens in seconds.</p><p>There are <a href="https://ghostscript.readthedocs.io/en/latest/Use.html?ref=admcpr.com#using-ghostscript-with-pdf-files">a lot of switches available</a> for the conversion process if your pdf is more complicated than mine were.</p>]]></content:encoded></item><item><title><![CDATA[Generate a wildcard SSL certificate behind a firewall with  Cloudflare & Docker]]></title><description><![CDATA[<p>The wonderful <a href="https://letsencrypt.org/?ref=admcpr.com">Let&apos;s Encrypt</a> makes it easy to easy to generate and install SSL certificates for free using the <a href="https://certbot.eff.org/?ref=admcpr.com">certbot</a> ACME client on a webserver. But what if, like me, you want to generate and install your certs on a server behind a firewall so that you can</p>]]></description><link>https://admcpr.com/generate-wildcard-ssl-cert-behind-a-firewall-with-synology-cloudflare-docker/</link><guid isPermaLink="false">62b324e330937a33b620c15c</guid><category><![CDATA[docker]]></category><category><![CDATA[SSH]]></category><dc:creator><![CDATA[Adam Cooper]]></dc:creator><pubDate>Thu, 23 Jun 2022 14:55:28 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1635602739175-bab409a6e94c?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDd8fHBhZGxvY2t8ZW58MHx8fHwxNjU1OTk2MDY0&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1635602739175-bab409a6e94c?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDd8fHBhZGxvY2t8ZW58MHx8fHwxNjU1OTk2MDY0&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Generate a wildcard SSL certificate behind a firewall with  Cloudflare &amp; Docker"><p>The wonderful <a href="https://letsencrypt.org/?ref=admcpr.com">Let&apos;s Encrypt</a> makes it easy to easy to generate and install SSL certificates for free using the <a href="https://certbot.eff.org/?ref=admcpr.com">certbot</a> ACME client on a webserver. But what if, like me, you want to generate and install your certs on a server behind a firewall so that you can use subdomains in your home network while keeping that network private. </p><p>Yep, they make that easy too because certbot supports dns challenge as a method of verifying your ownership of a domain and provides <a href="https://eff-certbot.readthedocs.io/en/stable/using.html?ref=admcpr.com#dns-plugins">dns plugins</a> for every major DNS provider. </p><hr><h3 id="how-dns-challenge-works-with-certbot">How DNS challenge works with certbot</h3><p>The <a href="https://eff-certbot.readthedocs.io/en/stable/using.html?ref=admcpr.com#plugins">certbot docs</a> provide a much better explanation of this than I ever could but TLDR:</p><ol><li>Certbot requests a certificate from the Let&apos;s Encrypt servers</li><li>Let&apos;s Encrypt servers return challenge contents</li><li>Plugin creates a TXT DNS record containing the challenge contents</li><li>Let&apos;s Encrypt servers validate the TXT DNS record</li><li>Let&apos;s Encrypt servers issue certificate</li></ol><hr><h3 id="lets-use-docker">Let&apos;s use docker</h3><p>In my case I use Cloudflare as my DNS provider and I&apos;m going to generate the cert on my trusty Synology NAS. Now I could manually install certbot, it&apos;s dependencies and the Cloudflare plugin, but the Synology has Docker installed and there&apos;s a <a href="https://hub.docker.com/r/certbot/dns-cloudflare?ref=admcpr.com">Docker image for the Cloudflare plugin</a> so that&apos;s much simpler.</p><h3 id="getting-started">Getting started</h3><p>The docker image needs to match a couple of letsencrypt volumes inside /var/lib and /etc/ so we&apos;ll start by creating them. </p><pre><code class="language-shell">sudo mkdir /etc/letsencrypt
sudo mkdir /var/lib/letsencrypt</code></pre><h3 id="set-up-cloudflare-credentials">Set up Cloudflare credentials</h3><p>To enable certbot to create TXT records via the Cloudflare API we&apos;ll need to create an API token via the <a href="https://dash.cloudflare.com/profile/api-tokens?ref=admcpr.com">cloudflare dashboard</a>. We choose to create a token and select the <code>Edit zone DNS</code> template and create the token for my domain.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2022/06/image.png" class="kg-image" alt="Generate a wildcard SSL certificate behind a firewall with  Cloudflare &amp; Docker" loading="lazy" width="1193" height="1072" srcset="https://admcpr.com/content/images/size/w600/2022/06/image.png 600w, https://admcpr.com/content/images/size/w1000/2022/06/image.png 1000w, https://admcpr.com/content/images/2022/06/image.png 1193w" sizes="(min-width: 720px) 720px"><figcaption>Create token screenshot</figcaption></figure><div class="kg-card kg-callout-card kg-callout-card-yellow"><div class="kg-callout-emoji">&#x26A0;&#xFE0F;</div><div class="kg-callout-text">This token can edit your DNS, treat it like you would your password!</div></div><p>Now we have the token let&apos;s create a <code>cloudflare.ini</code> file that certbot will read the token from.</p><pre><code>sudo vi /etc/letsencrypt/cloudflare.ini</code></pre><p>The file contents should have the following format.</p><figure class="kg-card kg-code-card"><pre><code class="language-ini"># Cloudflare API token used by Certbot
dns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567</code></pre><figcaption>cloudflare.ini</figcaption></figure><p>Now we can run the docker container passing in the location of the <code>ini</code> file containing the api token and the name of the domain to generate a certificate for. I&apos;m using <code>*.admcpr.com</code> to generate a wildcard cert so I can use this cert for lots of different subdomains in my local network.</p><pre><code class="language-shell">sudo docker run -it --rm --name certbot \
            -v &quot;/etc/letsencrypt:/etc/letsencrypt&quot; \
            -v &quot;/var/lib/letsencrypt:/var/lib/letsencrypt&quot; \
            certbot/dns-cloudflare \
            certonly --dns-cloudflare \
                     --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
                     -d *.admcpr.com</code></pre><p>Once this command completes the cert can be found in <code>/etc/letsencrypt/live/$domain</code> where <code>$domain</code> is the name of the domain the cert was generated for, so for me that&apos;s <code>/etc/letsencrypt/live/admcpr.com</code>. Now we can grab that cert and install it wherever we want to have SSL on a subdomain.</p><h3 id="next-steps">Next steps</h3><p>Let&apos;s Encrypt certs are valid for 3 months so I&apos;ll need to setup some way to automate renewal and find a more secure place to store the Cloudflare API key. </p>]]></content:encoded></item><item><title><![CDATA[How to add HEVC support to Windows for free]]></title><description><![CDATA[<p>#TLDR <a href="#powershelltotherescue">use PowerShell to install an extension</a>.</p><p>If, like me, you&apos;re both and iPhone and a Windows user you&apos;ve probably come up against a friendly message telling you that.</p><blockquote>The HEVC Video Extension is required to display this file in full resolution. Download and Install it</blockquote>]]></description><link>https://admcpr.com/how-to-open-hevc-on-windows/</link><guid isPermaLink="false">62b053e430937a33b620c0b0</guid><dc:creator><![CDATA[Adam Cooper]]></dc:creator><pubDate>Mon, 31 Jan 2022 12:12:27 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1586034679970-cb7b5fc4928a?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fHNhdmV8ZW58MHx8fHwxNjQzNjMxMTIz&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1586034679970-cb7b5fc4928a?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fHNhdmV8ZW58MHx8fHwxNjQzNjMxMTIz&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="How to add HEVC support to Windows for free"><p>#TLDR <a href="#powershelltotherescue">use PowerShell to install an extension</a>.</p><p>If, like me, you&apos;re both and iPhone and a Windows user you&apos;ve probably come up against a friendly message telling you that.</p><blockquote>The HEVC Video Extension is required to display this file in full resolution. Download and Install it now.</blockquote><figure class="kg-card kg-image-card kg-width-wide"><img src="https://admcpr.com/content/images/2022/01/Screenshot-2022-01-24-113540.png" class="kg-image" alt="How to add HEVC support to Windows for free" loading="lazy" width="1398" height="310" srcset="https://admcpr.com/content/images/size/w600/2022/01/Screenshot-2022-01-24-113540.png 600w, https://admcpr.com/content/images/size/w1000/2022/01/Screenshot-2022-01-24-113540.png 1000w, https://admcpr.com/content/images/2022/01/Screenshot-2022-01-24-113540.png 1398w" sizes="(min-width: 1200px) 1200px"></figure><p><a href="https://en.wikipedia.org/wiki/High_Efficiency_Video_Coding?ref=admcpr.com">High Efficiency Video Coding</a> aka HEVC aka H.265 aka MPEG-H Part 2 is a video compression standard from those nice people at the MPEG-H standards group. It&apos;s the default video compression used when you&apos;re filming video on iOS and as a modern standard is of course natively supported on macOS, iOS, Android, and <s>Windows</s> <strong>not Windows</strong>. Yes, that&apos;s right despite making a profit of more than 20 billion dollars in 2021 cash strapped Microsoft can&apos;t afford to bundle the ability to play videos inside it&apos;s plucky upstart operating system. Or perhaps it can afford to, but it was too busy stuffing junk like Candy Crush into my start menu. </p><p>You may have clicked that download link and been disappointed to find that although you can &apos;Download and install&apos; the extension Microsoft expects you to pay for the privilege. You used to be able to get around this by just installing the &apos;HEVC Video Extensions from Device Manufacturer&apos; but conveniently those extensions are currently unavailable in the Microsoft Store.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://admcpr.com/content/images/2022/01/Screenshot-2022-01-24-113902.png" class="kg-image" alt="How to add HEVC support to Windows for free" loading="lazy" width="1753" height="446" srcset="https://admcpr.com/content/images/size/w600/2022/01/Screenshot-2022-01-24-113902.png 600w, https://admcpr.com/content/images/size/w1000/2022/01/Screenshot-2022-01-24-113902.png 1000w, https://admcpr.com/content/images/size/w1600/2022/01/Screenshot-2022-01-24-113902.png 1600w, https://admcpr.com/content/images/2022/01/Screenshot-2022-01-24-113902.png 1753w" sizes="(min-width: 1200px) 1200px"></figure><h3 id="powershell-to-the-rescue">PowerShell to the rescue</h3><p>You can install the extensions from a PowerShell session without needing to redeem a code with the following command.</p><p><code>start ms-windows-store://pdp/?ProductId=9n4wgh0z6vhq</code></p><p>This will pop open the Microsoft Store page for &apos;HEVC Video Extensions from Device Manufacturer&apos; and allow you to hit the Install button.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://admcpr.com/content/images/2022/08/image.png" class="kg-image" alt="How to add HEVC support to Windows for free" loading="lazy" width="1664" height="749" srcset="https://admcpr.com/content/images/size/w600/2022/08/image.png 600w, https://admcpr.com/content/images/size/w1000/2022/08/image.png 1000w, https://admcpr.com/content/images/size/w1600/2022/08/image.png 1600w, https://admcpr.com/content/images/2022/08/image.png 1664w" sizes="(min-width: 1200px) 1200px"></figure><h3 id="disclaimer">Disclaimer</h3><p>Using PowerShell seems to skip straight to installation ignoring the store&apos;s requirement for you to have a code to redeem, I don&apos;t know if it violates any licensing. </p><p>The cost of the HEVC extensions in the store is about 99 cents was it really worth the effort for me to write this post and you to read it? Probably not, but one thing that is for sure is that Microsoft could easily eat this cost, keep its users happy and stop forcing them to jump through hoops to have basic functionality. </p>]]></content:encoded></item><item><title><![CDATA[Pimp my WSL]]></title><description><![CDATA[How to jazz up your WSL shell with fish and oh-my-fish.]]></description><link>https://admcpr.com/pimp-my-wsl/</link><guid isPermaLink="false">62b053e430937a33b620c0a7</guid><category><![CDATA[Windows Subsytem for Linux]]></category><category><![CDATA[git]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Adam Cooper]]></dc:creator><pubDate>Mon, 24 Jan 2022 17:12:36 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1605812280606-22f3650ed7b1?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MXwxMTc3M3wwfDF8c2VhcmNofDJ8fHBpbXB8ZW58MHx8fA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1605812280606-22f3650ed7b1?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MXwxMTc3M3wwfDF8c2VhcmNofDJ8fHBpbXB8ZW58MHx8fA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Pimp my WSL"><p>How to jazz up your WSL shell with fish and oh-my-fish.</p><h2 id="install-a-fancier-shell">Install a fancier shell</h2><p>Bash (<a href="https://en.wikipedia.org/wiki/Bash_(Unix_shell)?ref=admcpr.com">Bourne Again Shell)</a> is the default shell in most Linux distributions so when you fire up WSL, you&apos;ll find yourself at a bash prompt. While it&apos;s a great default, it&apos;s pretty dull to look at and out of the box is missing features that I really love such as case insensitive <a href="https://fishshell.com/docs/current/interactive.html?ref=admcpr.com#autosuggestions">autosuggestion</a> and <a href="https://fishshell.com/docs/current/interactive.html?ref=admcpr.com#tab-completion">tab completion</a>. </p><p>Fortunately, <a href="https://fishshell.com/?ref=admcpr.com">fish shell</a> comes with these features, and <a href="https://fishshell.com/docs/current/index.html?ref=admcpr.com">plenty more</a> so let&apos;s start by installing it. I&apos;m using Ubuntu here, but you can find installation instructions for most distros on the <a href="https://fishshell.com/?ref=admcpr.com">fish homepage</a>.</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">sudo apt install fish</code></pre><figcaption>Install fish</figcaption></figure><p>Once apt&apos;s done its thing when I run the fish command, I should jump into a friendly new fish shell complete with helpful auto complete and even some nice git integration.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://admcpr.com/content/images/2022/01/image-2.png" class="kg-image" alt="Pimp my WSL" loading="lazy" width="1113" height="262" srcset="https://admcpr.com/content/images/size/w600/2022/01/image-2.png 600w, https://admcpr.com/content/images/size/w1000/2022/01/image-2.png 1000w, https://admcpr.com/content/images/2022/01/image-2.png 1113w"><figcaption>fish doesn&apos;t care if you&apos;ve got your casing right it&apos;ll help you anyway</figcaption></figure><p>There are of course <a href="https://fishshell.com/docs/current/faq.html?highlight=fisher&amp;ref=admcpr.com#where-can-i-find-extra-tools-for-fish">plugin/package managers</a> for fish that extend the core functionality. Personally, the only extra functionality I need is easy prompt theming so let&apos;s install <a href="https://github.com/oh-my-fish/oh-my-fish?ref=admcpr.com">oh-my-fish</a> for that.</p><figure class="kg-card kg-code-card"><pre><code class="language-fish">curl https://raw.githubusercontent.com/oh-my-fish/oh-my-fish/master/bin/install | fish</code></pre><figcaption>Install oh-my-fish</figcaption></figure><p>Now I can list and select available themes (I can also <a href="https://github.com/oh-my-fish/oh-my-fish/blob/master/docs/Themes.md?ref=admcpr.com">preview them on github</a>) and I can install my favourite, sashimi, as well as the <a href="https://draculatheme.com/fish?ref=admcpr.com">dracula</a> colour scheme.</p><figure class="kg-card kg-code-card"><pre><code class="language-fish">omf install sashimi
omf theme sashimi
omf install https://github.com/dracula/fish</code></pre><figcaption>Install sashimi and dracula</figcaption></figure><p>And now I have my shell just the way I like it.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://admcpr.com/content/images/2022/01/image-4.png" class="kg-image" alt="Pimp my WSL" loading="lazy" width="1273" height="171" srcset="https://admcpr.com/content/images/size/w600/2022/01/image-4.png 600w, https://admcpr.com/content/images/size/w1000/2022/01/image-4.png 1000w, https://admcpr.com/content/images/2022/01/image-4.png 1273w" sizes="(min-width: 1200px) 1200px"></figure><p>Finally, I&apos;ll set fish as my default shell, so it loads whenever I open this wsl distro, if fish was installed to a different location by your distro, you can always find it using the <code>whereis fish</code> command.</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">chsh -s /usr/bin/fish</code></pre><figcaption>Make fish my default shell</figcaption></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://rmpr.xyz/the-fish-shell-is-amazing/?ref=admcpr.com"><div class="kg-bookmark-content"><div class="kg-bookmark-title">The fish shell is amazing</div><div class="kg-bookmark-description">I&#x2019;ve been lurking the fish shell for a couple of years now (and thenushell but it is another story foranother time). Not so long ago, I decided to try it, and it&#x2019;s simply&#x2026; amazing. If I had to state one feature that makes me like to use it, it&#x2019;ll be theautocompletion, hands down. It&#x2019;s the first t&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://rmpr.xyz/the-fish-shell-is-amazing/favicon.png" alt="Pimp my WSL"><span class="kg-bookmark-publisher">Rufus Mairo</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://asciinema.org/a/447626.svg" alt="Pimp my WSL"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[Where to find royalty free photos, artworks and illustrations]]></title><description><![CDATA[<p>It used to be the case that acquiring royalty free stock images or illustrations meant paying hundreds of dollars. Fortunately, thanks to the world of open source and public domain art and design, those times are behind us and there are now hundreds of thousands of high-quality images freely available</p>]]></description><link>https://admcpr.com/where-to-find-royalty-free-images/</link><guid isPermaLink="false">62b053e430937a33b620c0a8</guid><category><![CDATA[Links]]></category><dc:creator><![CDATA[Adam Cooper]]></dc:creator><pubDate>Wed, 13 Oct 2021 12:55:43 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1569017388730-020b5f80a004?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDEyfHxvcGVuJTIwc291cmNlfGVufDB8fHx8MTYzNDEyOTU3Ng&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1569017388730-020b5f80a004?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDEyfHxvcGVuJTIwc291cmNlfGVufDB8fHx8MTYzNDEyOTU3Ng&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Where to find royalty free photos, artworks and illustrations"><p>It used to be the case that acquiring royalty free stock images or illustrations meant paying hundreds of dollars. Fortunately, thanks to the world of open source and public domain art and design, those times are behind us and there are now hundreds of thousands of high-quality images freely available for both personnel and commercial use. This is a list of some of my favourite sites for finding photos, illustrations and videos that are free to use for personal or professional projects. </p><figure class="kg-card kg-image-card kg-width-full kg-card-hascaption"><img src="https://admcpr.com/content/images/2021/10/pexels-josa-busseck-4763552-cropped.jpg" class="kg-image" alt="Where to find royalty free photos, artworks and illustrations" loading="lazy" width="2000" height="845" srcset="https://admcpr.com/content/images/size/w600/2021/10/pexels-josa-busseck-4763552-cropped.jpg 600w, https://admcpr.com/content/images/size/w1000/2021/10/pexels-josa-busseck-4763552-cropped.jpg 1000w, https://admcpr.com/content/images/size/w1600/2021/10/pexels-josa-busseck-4763552-cropped.jpg 1600w, https://admcpr.com/content/images/2021/10/pexels-josa-busseck-4763552-cropped.jpg 2000w"><figcaption><a href="https://www.pexels.com/photo/pink-and-white-jellyfish-illustration-4763552/?ref=admcpr.com">Pink and White Jellyfish Illustration</a></figcaption></figure><h2 id="pexels"><a href="https://www.pexels.com/?ref=admcpr.com">Pexels</a></h2><h5 id="license-free-to-use-attribution-not-required">License: <a href="https://www.pexels.com/license/?ref=admcpr.com">Free to use, attribution not required</a></h5><h5 id="types-photos-videos">Types: Photos, Videos</h5><p>Pexels is a site where contributors upload their photos and videos to share with the community and the world at large. To encourage contributions Pexels runs regular <a href="https://www.pexels.com/challenges/?ref=admcpr.com">challenges</a> with cash or sponsored prizes. Their catalogue contains over two hundred thousand photos along with thousands of short video clips.</p><h4 id="any-drawbacks">Any drawbacks?</h4><p>Not really. </p><figure class="kg-card kg-image-card kg-width-full kg-card-hascaption"><img src="https://admcpr.com/content/images/2021/10/502866ldsdl-cropped-1.jpg" class="kg-image" alt="Where to find royalty free photos, artworks and illustrations" loading="lazy" width="2000" height="766" srcset="https://admcpr.com/content/images/size/w600/2021/10/502866ldsdl-cropped-1.jpg 600w, https://admcpr.com/content/images/size/w1000/2021/10/502866ldsdl-cropped-1.jpg 1000w, https://admcpr.com/content/images/size/w1600/2021/10/502866ldsdl-cropped-1.jpg 1600w, https://admcpr.com/content/images/2021/10/502866ldsdl-cropped-1.jpg 2000w"><figcaption><a href="https://artvee.com/dl/a-southern-landscape-with-palms-in-the-evening-light/?ref=admcpr.com">A Southern Landscape With Palms In The Evening Light</a></figcaption></figure><h2 id="artvee"><a href="https://artvee.com/?ref=admcpr.com">Artvee</a></h2><h5 id="license-free-to-use-attribution-not-required-1">License: <a href="https://artvee.com/about-us/?ref=admcpr.com">Free to use, attribution not required</a></h5><h5 id="types-artworks-illustrations">Types: Artworks, Illustrations</h5><p>Artvee aggregates tens of thousands of scanned art and design images that have been made available by museums and libraries around the world including the Smithsonian and the Rijksmuseum. You can browse by category as well as search by artist or keyword.</p><h4 id="any-drawbacks-1">Any drawbacks?</h4><p>Copyright for public domain art is complicated and country specific, everything on artvee is public domain in the US and EU but not necessarily globally. </p><figure class="kg-card kg-image-card kg-width-full kg-card-hascaption"><img src="https://admcpr.com/content/images/2021/10/image-from-rawpixel-id-2478164-jpeg.jpg" class="kg-image" alt="Where to find royalty free photos, artworks and illustrations" loading="lazy" width="2000" height="770" srcset="https://admcpr.com/content/images/size/w600/2021/10/image-from-rawpixel-id-2478164-jpeg.jpg 600w, https://admcpr.com/content/images/size/w1000/2021/10/image-from-rawpixel-id-2478164-jpeg.jpg 1000w, https://admcpr.com/content/images/size/w1600/2021/10/image-from-rawpixel-id-2478164-jpeg.jpg 1600w, https://admcpr.com/content/images/size/w2400/2021/10/image-from-rawpixel-id-2478164-jpeg.jpg 2400w"><figcaption><a href="https://www.rawpixel.com/image/2478164/free-illustration-image-pattern-texture-floral?ref=admcpr.com">Venetian pattern by William Morris. Original from The Smithsonian Institution.&#xA0;</a></figcaption></figure><h2 id="rawpixel-public-domain"><a href="https://www.rawpixel.com/public-domain?ref=admcpr.com">rawpixel Public Domain</a></h2><h5 id="license-free-to-use-attribution-not-required-cc0-">License: <a href="https://www.rawpixel.com/services/licenses?ref=admcpr.com">Free to use, attribution not required</a> (CC0)</h5><h5 id="types-artworks-illustrations-1">Types: Artworks, Illustrations</h5><p>Rawpixel is a marketplace for stock imagery and design resources who also provide a collection of public domain art and design resources like Artvee.</p><h4 id="any-drawbacks-2">Any drawbacks?</h4><p>Copyright for public domain art is complicated and country specific, everything on rawpixel Public Domain is public domain in the US and EU but not necessarily globally.</p><p>You must sign up for an account to download images.</p><figure class="kg-card kg-image-card kg-width-full kg-card-hascaption"><img src="https://admcpr.com/content/images/2021/10/faye-cornish-n3XTxxV7qhI-unsplash-cropped.jpg" class="kg-image" alt="Where to find royalty free photos, artworks and illustrations" loading="lazy" width="2000" height="849" srcset="https://admcpr.com/content/images/size/w600/2021/10/faye-cornish-n3XTxxV7qhI-unsplash-cropped.jpg 600w, https://admcpr.com/content/images/size/w1000/2021/10/faye-cornish-n3XTxxV7qhI-unsplash-cropped.jpg 1000w, https://admcpr.com/content/images/size/w1600/2021/10/faye-cornish-n3XTxxV7qhI-unsplash-cropped.jpg 1600w, https://admcpr.com/content/images/2021/10/faye-cornish-n3XTxxV7qhI-unsplash-cropped.jpg 2000w"><figcaption><a href="https://unsplash.com/photos/n3XTxxV7qhI?ref=admcpr.com">Red kayaks on a mountain lake</a></figcaption></figure><h2 id="unsplash"><a href="https://unsplash.com/?ref=admcpr.com">Unsplash</a></h2><h5 id="license-free-to-use-attribution-not-required-2">License: <a href="https://unsplash.com/license?ref=admcpr.com">Free to use, attribution not required</a></h5><h5 id="types-photos-3d-renders">Types: Photos, 3D Renders</h5><p>The elder statesperson of the open-source image world Unsplash boasts over two million photos contributed by the site&apos;s users. </p><h4 id="any-drawbacks-3">Any drawbacks?</h4><p>Not really.</p><p></p><figure class="kg-card kg-image-card kg-width-full kg-card-hascaption"><img src="https://admcpr.com/content/images/2021/10/undraw_Shared_workspace_re_3gsu-1-.png" class="kg-image" alt="Where to find royalty free photos, artworks and illustrations" loading="lazy" width="1043" height="419" srcset="https://admcpr.com/content/images/size/w600/2021/10/undraw_Shared_workspace_re_3gsu-1-.png 600w, https://admcpr.com/content/images/size/w1000/2021/10/undraw_Shared_workspace_re_3gsu-1-.png 1000w, https://admcpr.com/content/images/2021/10/undraw_Shared_workspace_re_3gsu-1-.png 1043w"><figcaption>Shared workspace</figcaption></figure><h2 id="undraw"><a href="https://undraw.co/?ref=admcpr.com">Undraw</a></h2><h5 id="license-free-to-use-attribution-not-required-3">License: <a href="https://undraw.co/license?ref=admcpr.com">Free to use, attribution not required</a></h5><h5 id="types-vector-illustrations">Types: Vector Illustrations</h5><p>Launched in 2017 Undraw is an incredible collection of more than 1200 vector illustrations single handedly created by the designer <a href="https://twitter.com/ninaLimpi?ref=admcpr.com" rel="noopener noreferrer">Katerina Limpitsouni</a>. You can customise the colours of the illustration live on the website and download them in .svg or high resolution .png format. </p><h4 id="any-drawbacks-4">Any drawbacks?</h4><p>Nope.</p><p></p><figure class="kg-card kg-image-card kg-width-full kg-card-hascaption"><img src="https://admcpr.com/content/images/2020/10/user_profile_monochromatic.svg" class="kg-image" alt="Where to find royalty free photos, artworks and illustrations" loading="lazy"><figcaption><a href="https://www.manypixels.co/gallery/?page=18&amp;style=image&amp;ref=admcpr.com">User Profile</a></figcaption></figure><h2 id="manypixels-illustration-gallery"><a href="https://www.manypixels.co/gallery/?ref=admcpr.com">ManyPixels Illustration Gallery</a></h2><h5 id="license-free-to-use-attribution-not-required-4">License: <a href="https://www.manypixels.co/gallery/license?ref=admcpr.com">Free to use, attribution not required</a></h5><h5 id="types-vector-illustrations-1">Types: Vector Illustrations</h5><p>ManyPixels is an on-demand graphic design service that also provide a free illustration gallery. You can customise the color of the illustrations and download them in .svg or .png format.</p><h4 id="any-drawbacks-5">Any drawbacks?</h4><p>Not really.</p><figure class="kg-card kg-image-card kg-width-full kg-card-hascaption"><img src="https://admcpr.com/content/images/2021/10/superhero-534120_1920-cropped.jpg" class="kg-image" alt="Where to find royalty free photos, artworks and illustrations" loading="lazy" width="1920" height="657" srcset="https://admcpr.com/content/images/size/w600/2021/10/superhero-534120_1920-cropped.jpg 600w, https://admcpr.com/content/images/size/w1000/2021/10/superhero-534120_1920-cropped.jpg 1000w, https://admcpr.com/content/images/size/w1600/2021/10/superhero-534120_1920-cropped.jpg 1600w, https://admcpr.com/content/images/2021/10/superhero-534120_1920-cropped.jpg 1920w"><figcaption><a href="https://pixabay.com/illustrations/superhero-girl-speed-runner-534120/?ref=admcpr.com">Superhero girl speed runner</a></figcaption></figure><h2 id="pixabay"><a href="https://pixabay.com/?ref=admcpr.com">PixaBay</a></h2><h5 id="license-free-to-use-attribution-not-required-5">License: <a href="https://pixabay.com/service/license/?ref=admcpr.com">Free to use attribution not required</a></h5><h5 id="types-photos-illustrations-vector-illustrations-videos-music-sound-effects">Types: Photos, Illustrations, Vector Illustrations, Videos, Music, Sound Effects &#xA0;</h5><p>Pixabay is a community similar to Pexels and Unsplash where almost two and a half million images, videos, music and sound effects have been uploaded by creators around the world.</p><h4 id="any-drawbacks-6">Any drawbacks?</h4><p>You have to log in to download the highest resolution images.</p><figure class="kg-card kg-image-card kg-width-full kg-card-hascaption"><img src="https://admcpr.com/content/images/2020/10/Asset-4.svg" class="kg-image" alt="Where to find royalty free photos, artworks and illustrations" loading="lazy"><figcaption><a href="https://www.drawkit.io/product/nature-ecology-illustrations?ref=admcpr.com">Nature &amp; Ecology Illustrations</a></figcaption></figure><h2 id="drawkit"><a href="https://www.drawkit.io/?ref=admcpr.com">DrawKit</a></h2><h5 id="license-free-to-use-attribution-not-required-6">License: <a href="https://www.drawkit.io/license?ref=admcpr.com">Free to use, attribution not required</a></h5><h5 id="types-vector-illustrations-icons">Types: Vector Illustrations, Icons</h5><p>DrawKit is a project from designstripe that offers both free and premium collections of icons and vector images. Their free kits include more than two hundred illustrations and three hundred icons each available in 4 distinct colour styles.</p><h4 id="any-drawbacks-7">Any drawbacks?</h4><p>You must sign up for an account to download.</p><p>There are some frustrating prohibitions in the license e.g. downloads can&apos;t be used in web templates or app templates.</p><figure class="kg-card kg-image-card kg-width-full kg-card-hascaption"><img src="https://admcpr.com/content/images/2021/10/yoga-1.png" class="kg-image" alt="Where to find royalty free photos, artworks and illustrations" loading="lazy" width="1599" height="521" srcset="https://admcpr.com/content/images/size/w600/2021/10/yoga-1.png 600w, https://admcpr.com/content/images/size/w1000/2021/10/yoga-1.png 1000w, https://admcpr.com/content/images/2021/10/yoga-1.png 1599w"><figcaption>Yoga</figcaption></figure><h2 id="pixeltrue-free-illustrations"><a href="https://www.pixeltrue.com/free-illustrations?ref=admcpr.com">Pixeltrue - Free Illustrations</a></h2><h5 id="license-free-to-use-attribution-not-required-7">License: <a href="https://www.pixeltrue.com/free-illustrations?ref=admcpr.com">Free to use attribution not required</a></h5><h5 id="types-vector-illustrations-vector-animations">Types: Vector Illustrations, Vector Animations</h5><p>Pixeltrue is a small commercial illustration team who also make more than five hundred illustrations and animations available for free. </p><h4 id="any-drawbacks-8">Any drawbacks?</h4><p>There are some frustrating prohibitions in the license e.g. downloads can&apos;t be used in web templates or app templates.</p><p></p><h2></h2>]]></content:encoded></item><item><title><![CDATA[How to debug Docker build failures]]></title><description><![CDATA[Temporarily disable BuildKit so you can debug Docker builds ]]></description><link>https://admcpr.com/how-to-debug-docker-build-failures/</link><guid isPermaLink="false">62b053e430937a33b620c0a2</guid><category><![CDATA[docker]]></category><category><![CDATA[Linux]]></category><category><![CDATA[macOS]]></category><category><![CDATA[Powershell]]></category><dc:creator><![CDATA[Adam Cooper]]></dc:creator><pubDate>Wed, 29 Sep 2021 13:29:37 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1567427018141-0584cfcbf1b8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fG1pY3Jvc2NvcGV8ZW58MHx8fHwxNjMyOTIyMTE3&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1567427018141-0584cfcbf1b8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fG1pY3Jvc2NvcGV8ZW58MHx8fHwxNjMyOTIyMTE3&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="How to debug Docker build failures"><p>When a docker build is failing for me I find that a great way to understand what&apos;s causing the failure is to jump into the container in an interactive terminal and have a poke around. This used to be easy because Docker would build an image and cache it for each step in your dockerfile and output the id for that image. Since &#xA0;<a href="https://docs.docker.com/develop/develop-images/build_enhancements/?ref=admcpr.com">BuildKit</a> became the default build tool those images are no longer made available and you just see an output like this.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://admcpr.com/content/images/2021/09/image-4.png" class="kg-image" alt="How to debug Docker build failures" loading="lazy" width="1878" height="382" srcset="https://admcpr.com/content/images/size/w600/2021/09/image-4.png 600w, https://admcpr.com/content/images/size/w1000/2021/09/image-4.png 1000w, https://admcpr.com/content/images/size/w1600/2021/09/image-4.png 1600w, https://admcpr.com/content/images/2021/09/image-4.png 1878w" sizes="(min-width: 1200px) 1200px"></figure><p>Now you could disable BuildKit in your docker config but that would make all your builds worse&#x2122; so instead what I do is temporarily disable BuildKit by setting the <code>DOCKER_BUILDKIT</code> environment variable to <code>0</code>. You can easily do this on the same line as your build command whatever shell you&apos;re using.</p><hr><h5 id="powershell">Powershell</h5><pre><code class="language-Powershell">$env:DOCKER_BUILDKIT=0; docker build .</code></pre><h5 id="linux-macos">Linux/macOS</h5><pre><code class="language-*sh">DOCKER_BUILDKIT=0 docker build .</code></pre><h5 id="windows-cmd">Windows CMD</h5><pre><code class="language-cmd">set DOCKER_BUILDKIT=0&amp; docker build .</code></pre><hr><p>This give will tell docker to use the legacy build tooling and so I&apos;ll get the id of each &#xA0;image for each step.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://admcpr.com/content/images/2021/09/image-5.png" class="kg-image" alt="How to debug Docker build failures" loading="lazy" width="1888" height="243" srcset="https://admcpr.com/content/images/size/w600/2021/09/image-5.png 600w, https://admcpr.com/content/images/size/w1000/2021/09/image-5.png 1000w, https://admcpr.com/content/images/size/w1600/2021/09/image-5.png 1600w, https://admcpr.com/content/images/2021/09/image-5.png 1888w" sizes="(min-width: 1200px) 1200px"></figure><p>So for the example above I can see that the last successful step is 1/2 and it produced an image <code>14119a10abf4</code>. I can run that image and connect to an interactive shell using the following command and debug away.</p><pre><code class="language-sh">docker run -it 14119a10abf4 sh</code></pre><p></p>]]></content:encoded></item><item><title><![CDATA[Dual boot multiple versions of Windows the easy way]]></title><description><![CDATA[How to easily dual boot two versions of Windows ]]></description><link>https://admcpr.com/dual-boot-two-versions-of-windows/</link><guid isPermaLink="false">62b053e430937a33b620c0ae</guid><category><![CDATA[Windows]]></category><category><![CDATA[Powershell]]></category><dc:creator><![CDATA[Adam Cooper]]></dc:creator><pubDate>Mon, 13 Sep 2021 13:01:09 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1601924638867-3a6de6b7a500?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDR8fGJvb3RzfGVufDB8fHx8MTYzNDE1MDY2OA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<h2 id="prerequisites">Prerequisites</h2><h3 id="a-windows-iso">A Windows ISO</h3><img src="https://images.unsplash.com/photo-1601924638867-3a6de6b7a500?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDR8fGJvb3RzfGVufDB8fHx8MTYzNDE1MDY2OA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Dual boot multiple versions of Windows the easy way"><p>There&apos;s plenty of other places you can find one but personally I would do one of the following.</p><ol><li>If you have a Microsoft account that&apos;s enrolled in the insiders program you can <a href="https://www.microsoft.com/en-us/software-download/windowsinsiderpreviewiso?ref=admcpr.com">download an insider preview ISO</a>.</li><li>If you just want a stable current version of you can download the <a href="https://www.microsoft.com/en-gb/software-download/windows10?ref=admcpr.com">media creation tool for Windows 10</a> and use it to generate an ISO.</li></ol><h2 id="installing-the-new-version">Installing the new version</h2><p>With the ISO downloaded let&apos;s start by mounting it, you could just right-click and choose <code>Mount</code> or you could use a powershell command instead.</p><pre><code class="language-powershell">Mount-DiskImage -ImagePath &quot;c:\Windows-Insider.iso&quot;</code></pre><p>Now the ISO is mounted let&apos;s have a quick look at my filesystem looks.</p><pre><code class="language-powershell">Get-PSDrive -PSProvider FileSystem</code></pre><figure class="kg-card kg-image-card"><img src="https://admcpr.com/content/images/2021/09/image.png" class="kg-image" alt="Dual boot multiple versions of Windows the easy way" loading="lazy" width="1480" height="216" srcset="https://admcpr.com/content/images/size/w600/2021/09/image.png 600w, https://admcpr.com/content/images/size/w1000/2021/09/image.png 1000w, https://admcpr.com/content/images/2021/09/image.png 1480w" sizes="(min-width: 720px) 720px"></figure><p>You can see I&apos;ve got Windows installed on the C: drive and F: is the mounted Windows install ISO. D: is where I&apos;m going to install my second version of Windows and E: is a spare.</p><p>We&apos;re going to use the <a href="https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism---deployment-image-servicing-and-management-technical-reference-for-windows?ref=admcpr.com">Deployment Image Servicing and Management</a> (<code>dism</code> ) to do this and the first job we&apos;ll use it for is finding out which images are available in the ISO using the <code>Get-WimInfo</code> command. Depending how that ISO was created that info is stored either in a .wim file or a .esd file. </p><p>If you used option 1. above it will be in a install.wim file so we use the comman</p><pre><code class="language-powershell"> # List available editions for .wim based ISO
 dism /Get-WimInfo /WimFile:F:\Sources\install.wim</code></pre><p>If you used option 2. above it will be in an install.esd file so we use the command:</p><pre><code class="language-powershell"> # List available editions for .esd based ISO
 dism /Get-WimInfo /WimFile:F:\Sources\install.esd</code></pre><p>This will print a list of all the editions of windows available within the ISO and the index of each, here&apos;s a snippet of the output I get.</p><figure class="kg-card kg-image-card"><img src="https://admcpr.com/content/images/2021/09/image-2.png" class="kg-image" alt="Dual boot multiple versions of Windows the easy way" loading="lazy" width="1915" height="283" srcset="https://admcpr.com/content/images/size/w600/2021/09/image-2.png 600w, https://admcpr.com/content/images/size/w1000/2021/09/image-2.png 1000w, https://admcpr.com/content/images/size/w1600/2021/09/image-2.png 1600w, https://admcpr.com/content/images/2021/09/image-2.png 1915w" sizes="(min-width: 720px) 720px"></figure><p>I&apos;m going to install the image at Index 6 (Windows 11 Pro) from the F drive to the D drive.</p><pre><code class="language-powershell">dism /Apply-Image /ImageFile:F:\Sources\install.wim /Index:6 /ApplyDir:D:\</code></pre><p>This will take a while, but you should see a nice progress bar like this:</p><figure class="kg-card kg-image-card"><img src="https://admcpr.com/content/images/2021/09/image-3.png" class="kg-image" alt="Dual boot multiple versions of Windows the easy way" loading="lazy" width="1890" height="177" srcset="https://admcpr.com/content/images/size/w600/2021/09/image-3.png 600w, https://admcpr.com/content/images/size/w1000/2021/09/image-3.png 1000w, https://admcpr.com/content/images/size/w1600/2021/09/image-3.png 1600w, https://admcpr.com/content/images/2021/09/image-3.png 1890w" sizes="(min-width: 720px) 720px"></figure><p>Once complete we just need to add the new installation to the boot menu.</p><pre><code class="language-powershell">bcdboot D:\Windows</code></pre><p>And, optionally, set its description. </p><pre><code class="language-powershell">bcdedit /set {default} description &quot;Windows 11 Insider Preview&quot;</code></pre><p>Now when you reboot, you&apos;ll get the choice of which Windows version to boot.</p>]]></content:encoded></item><item><title><![CDATA[Start on the right path]]></title><description><![CDATA[How to specify the startup path of your WSL distribution when launching from Powershell or Windows Terminal ]]></description><link>https://admcpr.com/start-on-the-right-path/</link><guid isPermaLink="false">62b053e430937a33b620c0a9</guid><category><![CDATA[Windows Subsytem for Linux]]></category><category><![CDATA[Windows]]></category><dc:creator><![CDATA[Adam Cooper]]></dc:creator><pubDate>Sat, 08 Aug 2020 20:44:18 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1546570089-777fb6b884c4?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1546570089-777fb6b884c4?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Start on the right path"><p>By default, and for reasons that I&apos;m sure make sense to someone, wsl distributions launch at your windows user&apos;s home path which for me is <code>/mnt/c/Users/adamc</code>. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2020/08/image-1.png" class="kg-image" alt="Start on the right path" loading="lazy"><figcaption>Default behaviour when I run wsl ... why?</figcaption></figure><p>I definitely don&apos;t want that, &#xA0;the performance of windows shares in wsl right now is not great and it&apos;s never going to be as fast as working inside the distro&apos;s file system. So I always aim to work inside the distro&apos;s filesystem and I want to start in my users home directory. WSL gives the option to specify the startup path when launching so running <code>wsl ~</code> will start the default installed distro in it&apos;s home directory. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2020/08/image-2.png" class="kg-image" alt="Start on the right path" loading="lazy"><figcaption>Yay a place I actually want to be</figcaption></figure><p>If you have multiple distros installed you can specify the distro as well as the path when you start for example <code>wsl ~ -d Debian</code> will open my Debian distro in my Debian user&apos;s home folder. </p><p>With Windows Terminal you can configure the command that launches your distro via the commandLine parameter of a <a href="https://docs.microsoft.com/en-us/windows/terminal/customize-settings/profile-settings?ref=admcpr.com">profile</a>. I have my terminal configured to launch Ubuntu and Debian like below which means when I launch either I always start where I want to be.</p><pre><code class="language-json">{
	&quot;guid&quot;: &quot;{2c4de342-38b7-51cf-b940-2309a097f518}&quot;,
	&quot;hidden&quot;: false,
	&quot;name&quot;: &quot;Ubuntu&quot;,
	&quot;source&quot;: &quot;Windows.Terminal.Wsl&quot;,
	&quot;commandline&quot;: &quot;wsl ~ -d Ubuntu&quot;
}
{
	&quot;guid&quot;: &quot;{58ad8b0c-3ef8-5f4d-bc6f-13e4c00f2530}&quot;,
	&quot;hidden&quot;: false,
	&quot;name&quot;: &quot;Debian&quot;,
	&quot;source&quot;: &quot;Windows.Terminal.Wsl&quot;,
	&quot;commandline&quot;: &quot;wsl ~ -d Debian&quot;
}</code></pre><p>You can also specify the <a href="https://docs.microsoft.com/en-us/windows/terminal/customize-settings/profile-settings?ref=admcpr.com#starting-directory">starting directory</a> when configuring a profile. This has to be a windows path but because wsl exposes every distro under the windows network path <code>\\wsl$\</code> &#xA0;you can access any path on any distro. So another way for me to configure my profiles would be the config below. </p><pre><code class="language-json">{
	&quot;guid&quot;: &quot;{2c4de342-38b7-51cf-b940-2309a097f518}&quot;,
	&quot;hidden&quot;: false,
	&quot;name&quot;: &quot;Ubuntu&quot;,
	&quot;source&quot;: &quot;Windows.Terminal.Wsl&quot;,
	&quot;startingDirectory&quot;: &quot;\\\\wsl$\\Ubuntu\\home\\adam&quot;
}
{
	&quot;guid&quot;: &quot;{58ad8b0c-3ef8-5f4d-bc6f-13e4c00f2530}&quot;,
	&quot;hidden&quot;: false,
	&quot;name&quot;: &quot;Debian&quot;,
	&quot;source&quot;: &quot;Windows.Terminal.Wsl&quot;,
	&quot;startingDirectory&quot;: &quot;\\\\wsl$\\Debian\\home\\adam&quot;
}</code></pre>]]></content:encoded></item><item><title><![CDATA[What the fork - How to switch to a fork after cloning a remote repository]]></title><description><![CDATA[How to switch to a fork after cloning a remote repository.
]]></description><link>https://admcpr.com/what-the-fork/</link><guid isPermaLink="false">62b053e430937a33b620c0a6</guid><category><![CDATA[git]]></category><dc:creator><![CDATA[Adam Cooper]]></dc:creator><pubDate>Fri, 19 Jun 2020 21:33:01 GMT</pubDate><media:content url="https://admcpr.com/content/images/2020/06/abstract-art-cooking-cutlery-262896-2.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://admcpr.com/content/images/2020/06/abstract-art-cooking-cutlery-262896-2.jpg" alt="What the fork - How to switch to a fork after cloning a remote repository"><p>I often clone a repo on GitHub or GitLab make some changes to it locally and then think I&apos;d like to contribute my changes back to the repo. At that point I always wish I could remember the steps to switch to a fork, so for the benefit of my future self this time I wrote down the steps.</p><h3 id="step-1-clone-a-repo">Step 1 - Clone a repo</h3><p>Most recently I thought I&apos;d have a look at how to contribute to the new winget package manager.</p><pre><code class="language-bash">git clone https://github.com/microsoft/winget-pkgs</code></pre><h3 id="step-2-make-some-changes">Step 2 - Make some changes</h3><p>I updated the version of <a href="https://pooi.moe/QuickLook/?ref=admcpr.com">QuickLook</a> and was ready to push and create a PR when ... </p><h3 id="step-3-realise-i-should-have-forked-first">Step 3 - Realise I should have forked first </h3><p>At this point I remember I have no permissions on this repo, and I should have started from a fork &#x1F926;&#x200D;&#x2642;&#xFE0F; </p><h3 id="step-4-fork">Step 4 - Fork</h3><p>So off to GitHub and hit the Fork button which gives me a forked repo at <code>https://github.com/adam7/winget-pkgs</code></p><h3 id="step-5-rename-my-origin-repo-to-upstream">Step 5 - Rename my origin repo to upstream</h3><p>I&apos;m going to want my fork to be origin so I can push there so let&apos;s rename the current origin.</p><pre><code class="language-bash">git remote rename origin upstream</code></pre><h3 id="step-6-make-my-fork-the-origin">Step 6 - Make my fork the origin</h3><p>Now I can just point origin at my newly forked repo via SSH:</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">git remote add origin git@github.com:adam7/winget-pkgs</code></pre><figcaption>remote add using ssh</figcaption></figure><p>or via HTTPS:</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">git remote add origin https://github.com/adam7/winget-pkgs</code></pre><figcaption>remote add using https</figcaption></figure><h3 id="step-7-fetch-from-my-new-origin">Step 7 - Fetch from my new origin</h3><pre><code class="language-bash">git fetch origin</code></pre><h3 id="step-8-set-origin-main-or-master-">Step 8 - Set origin main (or master)</h3><p>Set the upstream of my main branch to point to my fork. &#xA0;</p><pre><code class="language-bash">git branch --set-upstream-to origin/main main</code></pre><p>Or if it&apos;s an older repo with <code>master</code> instead of <code>main</code> as the default branch.</p><pre><code class="language-bash">git branch --set-upstream-to origin/master master</code></pre><h3 id="step-9-push-to-my-fork">Step 9 - Push to my fork </h3><p>And now I can push to my forked repo and create a PR.</p><pre><code class="language-bash">git push</code></pre><h3 id="step-10-optional-">Step 10 (Optional)</h3><p>Write this blog post</p>]]></content:encoded></item><item><title><![CDATA[Running docker desktop with the new WSL 2 backend]]></title><description><![CDATA[Running Docker on Windows has been easy for a long time, but it has always needed to run inside a Hyper-V virtual machine. 
Thanks to the Windows Subsystem for Linux 2 though it's now possible to run docker in a WSL distribution and avoid the need for Hyper-V altogether.]]></description><link>https://admcpr.com/running-docker-desktop-with-the-new-wsl-2-backend/</link><guid isPermaLink="false">62b053e430937a33b620c0a4</guid><category><![CDATA[Windows Subsytem for Linux]]></category><category><![CDATA[docker]]></category><dc:creator><![CDATA[Adam Cooper]]></dc:creator><pubDate>Mon, 04 May 2020 19:43:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1512380924987-27e737876395?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1512380924987-27e737876395?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Running docker desktop with the new WSL 2 backend"><p>Running Docker on Windows has been easy for a long time, but it has always needed to run inside a Hyper-V virtual machine. There are at least two, and probably more, problems with this approach: </p><ol><li>Hyper-V requires Windows Professional, a ludicrous limitation, which made docker (and lots of other great tools) unavailable to Windows Home users. </li><li>The CPU and memory resources allocated to the Hyper-V VM and therefore to your docker containers must be managed by you. &#xA0;Assign too many and you&apos;ll lose access to those resources from your Windows OS, assign too few and you&apos;ll see performance issues in your containers.</li></ol><p>Thanks to the Windows Subsystem for Linux 2 though it&apos;s now possible to run docker in a WSL distribution and avoid the need for Hyper-V altogether.</p><h2 id="stuff-you-ll-need">Stuff you&apos;ll need</h2><ol><li>Windows 10 Build 19041 or later.</li><li>Docker desktop version 2.2.0.5 or later.</li><li>WSL 2 <a href="https://docs.microsoft.com/en-us/windows/wsl/wsl2-install?ref=admcpr.com">installed</a>.</li></ol><h2 id="before-using-wsl">Before using WSL</h2><p>By default, docker desktop currently uses Hyper-V which means if you install it today and start it up, behind the scenes it will spin up a VM. If you run <code>Get-VM</code> from PowerShell you can see that VM has the rather un-mysterious name DockerDesktopVM.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://admcpr.com/content/images/2020/04/image-1.png" class="kg-image" alt="Running docker desktop with the new WSL 2 backend" loading="lazy"><figcaption>Get-VM</figcaption></figure><p>In my case docker has assigned 2GB of memory to the VM and if I run <code>Get-VMProcessor</code> I can see that my docker VM has two processors assigned.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://admcpr.com/content/images/2020/04/image-2.png" class="kg-image" alt="Running docker desktop with the new WSL 2 backend" loading="lazy"><figcaption>Get-VMProcessor</figcaption></figure><p>And if I run <code>docker info</code> I can see that those are the resources available to my docker server.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://admcpr.com/content/images/2020/05/image-5.png" class="kg-image" alt="Running docker desktop with the new WSL 2 backend" loading="lazy"><figcaption>docker info</figcaption></figure><p>If I open up docker desktop Settings-&gt;Resources tab, I&apos;ll see the same settings and from here I can manually configure those available resources. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2020/04/image-4.png" class="kg-image" alt="Running docker desktop with the new WSL 2 backend" loading="lazy"><figcaption>Settings -&gt; Resources</figcaption></figure><h2 id="enable-docker-wsl">Enable Docker WSL </h2><p>So that&apos;s a quick overview of how things work running docker in Hyper-V, now let&apos;s see how things are different with WSL. To enable the WSL engine, I just need to go to Settings -&gt; General and turn on <code>Enable the experimental WSL 2 based engine</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2020/05/image.png" class="kg-image" alt="Running docker desktop with the new WSL 2 backend" loading="lazy"><figcaption>Settings -&gt; General</figcaption></figure><p>Once I <code>Apply &amp; Restart</code> docker will restart and my DockerDesktopVM will no longer be running.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://admcpr.com/content/images/2020/05/image-1.png" class="kg-image" alt="Running docker desktop with the new WSL 2 backend" loading="lazy"></figure><p>Now instead I have two WSL distros running which I can see by running <code>wsl -l -v</code>.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://admcpr.com/content/images/2020/05/image-3.png" class="kg-image" alt="Running docker desktop with the new WSL 2 backend" loading="lazy"></figure><p>Two distros ... that&apos;s seems strange, why does docker use two WSL distros when it only needs one Hyper-V VM? The name is giving us a clue, the docker-desktop-data distro exists as a storage for images and configs, as well as the Kubernetes data store. Those are used by the docker-desktop distro, the same result is achieved when docker is run under Hyper-V by mounting a VHD (Virtual Hard Disk) in the Hyper-V image but mounting a VHD isn&apos;t possible with WSL2 yet.</p><p>But moving on to more exciting news let&apos;s run <code>docker info</code>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://admcpr.com/content/images/2020/05/image-6.png" class="kg-image" alt="Running docker desktop with the new WSL 2 backend" loading="lazy"><figcaption>docker info</figcaption></figure><p>Now I can see that docker has access to 8 CPU cores and 13GB of the 16GB installed in my laptop. Because docker is now running in a WSL distro it gets access to all the CPU cores and by default 80% of the RAM. This is the shared across all distros running under WSL 2 and the allocation of resources will be handled dynamically for you by WSL.</p><p>If you want to modify config settings for WSL 2 to explicitly limit the available resources you can do that by creating a <a href="https://docs.microsoft.com/en-us/windows/wsl/wsl-config?ref=admcpr.com#configure-global-options-with-wslconfig">.wslconfig</a> file inside your user profile directory and setting any/all of the parameters below. Personally, though I&apos;ve found that just leaving WSL alone to manage these for me has worked pretty smoothly. </p><figure class="kg-card kg-code-card"><pre><code class="language-ini">[wsl2]
kernel=&lt;path&gt;              # An absolute Windows path to a custom Linux kernel.
memory=&lt;size&gt;              # How much memory to assign to the WSL2 VM.
processors=&lt;number&gt;        # How many processors to assign to the WSL2 VM.
swap=&lt;size&gt;                # How much swap space to add to the WSL2 VM. 0 for no swap file.
swapFile=&lt;path&gt;            # An absolute Windows path to the swap vhd.
localhostForwarding=&lt;bool&gt; # Boolean specifying if ports bound to wildcard or localhost in the WSL2 VM should be connectable from the host via localhost:port (default true).

# &lt;path&gt; entries must be absolute Windows paths with escaped backslashes, for example C:\\Users\\adamc\\kernel
# &lt;size&gt; entries must be size followed by unit, for example 8GB or 512MB</code></pre><figcaption>.wslconfig</figcaption></figure><h2 id="accessing-docker-from-another-wsl-distribution">Accessing docker from another WSL distribution</h2><p>Navigating to Settings -&gt; Resources in docker desktop now that WSL integration is enabled I see can see that there&apos;s no longer an Advanced tab for configuring docker resources, which makes sense given what we&apos;ve just talked about. I also see that there&apos;s a new setting available for WSL Integration.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://admcpr.com/content/images/2020/05/image-7.png" class="kg-image" alt="Running docker desktop with the new WSL 2 backend" loading="lazy"><figcaption>WSL Integration</figcaption></figure><p>In this case I&apos;ve installed an Ubuntu distro from the Windows store, but you might see different distros listed or even none. If you don&apos;t see any listed even though you have a distro installed, you&apos;ll need to <a href="https://admcpr.com/how-to-upgrade-wsl-1-to-wsl-2/">upgrade your distro to WSL 2</a>. Toggling this switch and then hitting <code>Apply &amp; Restart</code> install the docker client tools in my Ubuntu distro and give it access to the docker server running inside my docker-desktop distro.</p><p>If I launch my Ubuntu distro and run <code>docker info</code> I can see that I have access to the same server that&apos;s available to me from Windows.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://admcpr.com/content/images/2020/05/image-8.png" class="kg-image" alt="Running docker desktop with the new WSL 2 backend" loading="lazy"></figure><p>For a fun way to prove that I can run a container from inside Ubuntu.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://admcpr.com/content/images/2020/05/image-9.png" class="kg-image" alt="Running docker desktop with the new WSL 2 backend" loading="lazy"></figure><p>Switch to Powershell in Windows and when I list my docker containers there I&apos;ll see the exact same container running.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://admcpr.com/content/images/2020/05/image-10.png" class="kg-image" alt="Running docker desktop with the new WSL 2 backend" loading="lazy"></figure><h2 id="wrapping-up">Wrapping up </h2><p>In summary, for me the new WSL backend for docker makes docker so much nicer to work with on Windows. I get all the performance I need along with sweet integration across Windows and any Linux distributions I have installed. On top of this docker now gets the ability to run on Windows Home, Microsoft have made that impossible for far too long but this a very welcome change.</p><p>If you want to find out more about the details of the docker desktop WSL 2 backend I thoroughly recommend <a href="https://www.docker.com/blog/new-docker-desktop-wsl2-backend/?ref=admcpr.com">Introducing the Docker Desktop WSL 2 Backend</a> on the docker blog. </p>]]></content:encoded></item><item><title><![CDATA[Backup and restore images from your Ghost blog using SCP]]></title><description><![CDATA[How to backup and restore images from your Ghost blog using ssh and scp]]></description><link>https://admcpr.com/backup-and-restore-images-from-your-ghost-blog-using-scp/</link><guid isPermaLink="false">62b053e430937a33b620c09f</guid><category><![CDATA[SSH]]></category><category><![CDATA[Digital Ocean]]></category><dc:creator><![CDATA[Adam Cooper]]></dc:creator><pubDate>Sat, 07 Mar 2020 10:27:56 GMT</pubDate><media:content url="https://admcpr.com/content/images/2020/04/scp-download.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://admcpr.com/content/images/2020/04/scp-download.jpg" alt="Backup and restore images from your Ghost blog using SCP"><p>Ghost provides an easy way to back up the content of your website, from the Labs page you can choose export your content which will <em>&apos;Download all of your posts and settings in a single, glorious JSON file&apos;. </em>However, the export doesn&apos;t include a vital part of your blog content, specifically any files that you&apos;ve uploaded. Frustratingly there is also no straightforward way to download all those files that I&apos;m aware of. If you have ssh access to the server hosting your Ghost blog however you can do this easily using scp.</p><p>The <strong><a href="https://en.wikipedia.org/wiki/Secure_copyhttps://en.wikipedia.org/wiki/Secure_copy?ref=admcpr.com">S</a></strong><a href="https://en.wikipedia.org/wiki/Secure_copyhttps://en.wikipedia.org/wiki/Secure_copy?ref=admcpr.com">ecure </a><strong><a href="https://en.wikipedia.org/wiki/Secure_copyhttps://en.wikipedia.org/wiki/Secure_copy?ref=admcpr.com">C</a></strong><a href="https://en.wikipedia.org/wiki/Secure_copyhttps://en.wikipedia.org/wiki/Secure_copy?ref=admcpr.com">opy </a><strong><a href="https://en.wikipedia.org/wiki/Secure_copyhttps://en.wikipedia.org/wiki/Secure_copy?ref=admcpr.com">P</a></strong><a href="https://en.wikipedia.org/wiki/Secure_copyhttps://en.wikipedia.org/wiki/Secure_copy?ref=admcpr.com">rotocol</a> is a network protocol, based on the BSD RCP protocol, which supports file transfers between hosts on a network. What that translates to in practice is a simple way to do file transfers over ssh and since <a href="https://admcpr.com/using-the-new-windows-10-ssh/">Windows now bundles &#xA0;ssh</a> like a real operating system you&apos;re pretty much guaranteed to have scp available whatever os you&apos;re currently using. </p><p>By default, <a href="https://ghost.org/docs/concepts/storage-adapters/?ref=admcpr.com">Ghost stores images</a> in the <code>content/images</code> folder of your installation organised into sub-folders by year and month. If, like me, you&apos;re hosting on a Digital Ocean Droplet then your images will be stored under <code>/var/www/ghost/content/images/</code>. So, to download all my images I simply create a local folder and scp all the files into that folder like this:</p><figure class="kg-card kg-code-card"><pre><code>mkdir ghost-images
scp -r myusername@46.101.125.10:/var/www/ghost/content/images ghost-images</code></pre><figcaption>Create directory and download</figcaption></figure><p>Specifying the -r flag tells scp to run recursively so all sub-folders are also downloaded and, depending on how many images you have this might take a while. </p><p>I was doing this download so that I could migrate from one server to another, so the next step was to upload all those images. To do that you just have to reverse the order of arguments you pass to scp: </p><pre><code>scp -r ghost-images myusername@46.101.125.10:/var/www/ghost/content/images </code></pre><p>Happy copying :) </p><h2></h2>]]></content:encoded></item></channel></rss>